{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using ART to Defend against Poisoning Attacks with Neural Cleanse\n",
    "\n",
    "Neural Cleanse is a method developed by [Wang et. al. (2019)](https://people.cs.uchicago.edu/~ravenben/publications/pdf/backdoor-sp19.pdf). Using this method, we show how ART can defend against poison input by:\n",
    "\n",
    "- filtering out potentially poisonous input\n",
    "- unlearning the backdoor by retraining\n",
    "- pruning the neural network of neurons associated with the backdoor\n",
    "- some combination of the above\n",
    "\n",
    "One main distinction is that this method allows us to identify the backdoor pattern, and investigate neurons associated with these backdoors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "from __future__ import absolute_import, division, print_function, unicode_literals\n",
    "\n",
    "import os, sys\n",
    "from os.path import abspath\n",
    "\n",
    "module_path = os.path.abspath(os.path.join('..'))\n",
    "if module_path not in sys.path:\n",
    "    sys.path.append(module_path)\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "import keras.backend as k\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D, Activation, Dropout\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "from mpl_toolkits import mplot3d\n",
    "\n",
    "from art.estimators.classification import KerasClassifier\n",
    "from art.attacks.poisoning import PoisoningAttackBackdoor\n",
    "from art.attacks.poisoning.perturbations import add_pattern_bd, add_single_bd, insert_image\n",
    "from art.utils import load_mnist, preprocess\n",
    "from art.defences.detector.poison import ActivationDefence\n",
    "from art.defences.transformer.poisoning import NeuralCleanse\n",
    "from art.estimators.poison_mitigation import KerasNeuralCleanse\n",
    "\n",
    "import tensorflow as tf\n",
    "if tf.executing_eagerly():\n",
    "    tf.compat.v1.disable_eager_execution()"
   ]
  },
  {
   "attachments": {
    "image.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAAAAAC/Hd2sAAAM82lDQ1BrQ0dDb2xvclNwYWNlR2VuZXJpY0dyYXlHYW1tYTJfMgAAWIWlVwdYU8kWnluS0BJ6lRI60gwoXUqkBpBeBFGJIZBACDEFAbEhiyu4dhHBsqKiKIsdgcWGBQtrB7sLuigo6+IqNixvEopYdt/7vnfzzb3/nXPOnDpnbgBQ5TAFAh4KAMjki4WBUfSEKQmJVNJdIAe0gTKwB8pMlkhAj4gIhSyAn8Vng2+uV+0AkT6v2UnX+pb+rxchhS1iwedxOHJTRKxMAJCJAJC6WQKhGAB5MzhvOlsskOIgiDUyYqJ8IU4CQE5pSFZ6GQWy+Wwhl0UNFDJzqYHMzEwm1dHekRohzErl8r5j9f97ZfIkI7rhUBJlRIfApz20vzCF6SfFrhDvZzH9o4fwk2xuXBjEPgCgJgLxpCiIgyGeKcmIpUNsC3FNqjAgFmIviG9yJEFSPAEATCuPExMPsSHEwfyZYeEQu0PMYYl8EyG2griSw2ZI8wRjhp3nihkxEEN92DNhVpSU3xoAfGIK289/cB5PzcgKkdpgAvFBUXa0/7DNeRzfsEFdeHs6MzgCYguIX7J5gVGD6xD0BOII6ZrwneDH54WFDvpFKGWLZP7Cd0K7mBMjzZkjAEQTsTAmatA2YkwqN4ABcQDEORxhUNSgv8SjAp6szmBMiO+FkqjYQR9JAWx+rHRNaV0sYAr9AwdjRWoCcQgTsEEWmAnvLMAHnYAKRIALsmUoDTBBJhxUaIEtHIGQiw+HEHKIQIaMQwi6RujDElIZAaRkgVTIyYNyw7NUkALlB+Wka2TBIX2Trtstm2MN6bOHw9dwO5DANw7ohXQORJNBh2wmB9qXCZ++cFYCaWkQj9YyKB8hs3XQBuqQ9T1DWrJktjBH5D7b5gvpfJAHZ0TDnuHaOA0fD4cHHop74jSZlBBy5AI72fxE2dyw1s+eS33rGdE6C9o62vvR8RqO4QkoJYbvPOghfyg+ImjNeyiTMST9lZ8r9CRWAkHpskjG9KoRK6gFwhlc1qXlff+StW+1232Rt/DRdSGrlJRv6gLqIlwlXCbcJ1wHVPj8g9BG6IboDuEu/N36blSyRmKQBkfWSAWwv8gNG3LyZFq+tfNzzgbX+WoFBBvhpMtWkVIz4eDKeEQj+ZNALIb3VJm03Ve5C/xab0t+kw6gti89fg5Qa1Qazn6Odhten3RNqSU/lb9CTyCYXpU/wBZ8pkrzwF4c9ioMFNjS9tJ6adtoNbQXtPufOWg3aH/S2mhbIOUptho7hB3BGrBGrBVQ4VsjdgJrkKEarAn+9v1Dhad9p8KlFcMaqmgpVTxUU6Nrf3Rk6aOiJeUfjnD6P9Tr6IqRZux/s2j0Ol92BPbnXUcxpThQSBRrihOFTkEoxvDnSPGByJRiQgmlaENqEMWS4kcZMxKP4VrnDWWY+8X+HrQ4AVKHK4Ev6y5MyCnlYA75+7WP1C+8lHrGHb2rEDLcVdxRPeF7vYj6xc6KhbJcMFsmL5Ltdr5MTvBF/YlkXQjOIFNlOfyObbgh7oAzYAcKB1ScjjvhPkN4sCsN9yVZpnBvSPXC/XBXaR/7oi+w/qv1o3cGm+hOtCT6Ey0/04l+xCBiAHw6SOeJ44jBELtJucTsHLH0kPfNEuQKuWkcMZUOv3LYVAafZW9LdaQ5wNNN+s00+CnwIlL2LYRotbIkwuzBOVx6IwAF+D2lAXThqWoKT2s7qNUFeMAz0x+ed+EgBuZ1OvSDA+0Wwsjmg4WgCJSAFWAtKAebwTZQDWrBfnAYNMEeewZcAJdBG7gDz5Mu8BT0gVdgAEEQEkJG1BFdxAgxR2wQR8QV8UL8kVAkCklAkpE0hI9IkHxkEVKCrELKkS1INbIPaUBOIOeQK8gtpBPpQf5G3qEYqoRqoAaoBToOdUXpaAgag05D09BZaB5aiC5Dy9BKtAatQ0+gF9A2tAN9ivZjAFPEtDBjzA5zxXyxcCwRS8WE2DysGCvFKrFa2ANasGtYB9aLvcWJuDpOxe1gFoPwWJyFz8Ln4UvxcnwnXoefwq/hnXgf/pFAJugTbAjuBAZhCiGNMJtQRCglVBEOEU7DDt1FeEUkErVgflxg3hKI6cQ5xKXEjcQ9xOPEK8SHxH4SiaRLsiF5ksJJTJKYVERaT6ohHSNdJXWR3sgpyhnJOcoFyCXK8eUK5Erldskdlbsq91huQF5F3lzeXT5cPkU+V365/Db5RvlL8l3yAwqqCpYKngoxCukKCxXKFGoVTivcVXihqKhoouimGKnIVVygWKa4V/GsYqfiWyU1JWslX6UkJYnSMqUdSseVbim9IJPJFmQfciJZTF5GriafJN8nv6GoU+wpDEoKZT6lglJHuUp5piyvbK5MV56unKdcqnxA+ZJyr4q8ioWKrwpTZZ5KhUqDyg2VflV1VQfVcNVM1aWqu1TPqXarkdQs1PzVUtQK1baqnVR7qI6pm6r7qrPUF6lvUz+t3qVB1LDUYGika5Ro/KJxUaNPU01zgmacZo5mheYRzQ4tTMtCi6HF01qutV+rXeudtoE2XZutvUS7Vvuq9mudMTo+OmydYp09Om0673Spuv66GbordQ/r3tPD9az1IvVm623SO63XO0ZjjMcY1pjiMfvH3NZH9a31o/Tn6G/Vb9XvNzA0CDQQGKw3OGnQa6hl6GOYbrjG8Khhj5G6kZcR12iN0TGjJ1RNKp3Ko5ZRT1H7jPWNg4wlxluMLxoPmFiaxJoUmOwxuWeqYOpqmmq6xrTZtM/MyGyyWb7ZbrPb5vLmruYc83XmLeavLSwt4i0WWxy26LbUsWRY5lnutrxrRbbytpplVWl1fSxxrOvYjLEbx162Rq2drDnWFdaXbFAbZxuuzUabK7YEWzdbvm2l7Q07JTu6XbbdbrtOey37UPsC+8P2z8aZjUsct3Jcy7iPNCcaD55udxzUHIIdChwaHf52tHZkOVY4Xh9PHh8wfv74+vHPJ9hMYE/YNOGmk7rTZKfFTs1OH5xdnIXOtc49LmYuyS4bXG64arhGuC51PetGcJvkNt+tye2tu7O72H2/+18edh4ZHrs8uidaTmRP3DbxoaeJJ9Nzi2eHF9Ur2etnrw5vY2+md6X3Ax9TnxSfKp/H9LH0dHoN/dkk2iThpEOTXvu6+871Pe6H+QX6Fftd9Ffzj/Uv978fYBKQFrA7oC/QKXBO4PEgQlBI0MqgGwwDBotRzegLdgmeG3wqRCkkOqQ85EGodagwtHEyOjl48urJd8PMw/hhh8NBOCN8dfi9CMuIWRG/RhIjIyIrIh9FOUTlR7VEq0fPiN4V/SpmUszymDuxVrGS2OY45bikuOq41/F+8aviO6aMmzJ3yoUEvQRuQn0iKTEusSqxf6r/1LVTu5KckoqS2qdZTsuZdm663nTe9CMzlGcwZxxIJiTHJ+9Kfs8MZ1Yy+2cyZm6Y2cfyZa1jPU3xSVmT0sP2ZK9iP071TF2V2p3mmbY6rYfjzSnl9HJ9ueXc5+lB6ZvTX2eEZ+zI+MSL5+3JlMtMzmzgq/Ez+KeyDLNysq4IbARFgo5Z7rPWzuoThgirRIhomqherAH/YLZKrCQ/SDqzvbIrst/Mjpt9IEc1h5/TmmuduyT3cV5A3vY5+BzWnOZ84/yF+Z1z6XO3zEPmzZzXPN90fuH8rgWBC3YuVFiYsfC3AlrBqoKXi+IXNRYaFC4ofPhD4A+7iyhFwqIbiz0Wb/4R/5H748Ul45esX/KxOKX4fAmtpLTk/VLW0vM/OfxU9tOnZanLLi53Xr5pBXEFf0X7Su+VO1eprspb9XD15NV1a6hrite8XDtj7bnSCaWb1ymsk6zrKAstq19vtn7F+vflnPK2ikkVezbob1iy4fXGlI1XN/lsqt1ssLlk87ufuT/f3BK4pa7SorJ0K3Fr9tZH2+K2tWx33V5dpVdVUvVhB39Hx86onaeqXaqrd+nvWr4b3S3Z3VOTVHP5F79f6mvtarfs0dpTshfslex9si95X/v+kP3NB1wP1B40P7jhkPqh4jqkLreu7zDncEd9Qv2VhuCG5kaPxkO/2v+6o8m4qeKI5pHlRxWOFh79dCzvWP9xwfHeE2knHjbPaL5zcsrJ66ciT108HXL67JmAMydb6C3HznqebTrnfq7hvOv5wxecL9S1OrUe+s3pt0MXnS/WXXK5VH/Z7XLjlYlXjl71vnrimt+1M9cZ1y+0hbVdaY9tv3kj6UbHzZSb3bd4t57fzr49cGcB/Igvvqdyr/S+/v3K38f+vqfDueNIp19n64PoB3cesh4+/UP0x/uuwkfkR6WPjR5Xdzt2N/UE9Fx+MvVJ11PB04Heoj9V/9zwzOrZwb98/mrtm9LX9Vz4/NPfS1/ovtjxcsLL5v6I/vuvMl8NvC5+o/tm51vXty3v4t89Hpj9nvS+7MPYD40fQz7e/ZT56dN/AC1d8BzqtvWAAABAAElEQVR4Aey9CZAkWXkm6PcR7nHfR95ZmVXdTR8wMGKkhkYLTJswLbpsJEMGKzC0iDGaFUKzSCvQohYDmhVIIA4zHQOrmZZk2hUIDcZoEIxoWECtg0HdQNeRWVl5x32HR/jt+3tkVt5HZMSLCM9q927L8nB/x/++5987/ve//+EW5l4uAi4CpyFAnPbCfe4i4CKAYS5B3K/AReAMBFyCnAGO+8pFwCWI+w24CJyBgEuQM8BxX7kIuARxvwEXgTMQcAlyBjjuKxcBlyDuN+AicAYCLkHOAMd95SLgEsT9BlwEzkDAJcgZ4LivXARcgrjfgIvAGQi4BDkDHPeVi4BLEPcbcBE4AwGXIGeA475yEXAJ4n4DLgJnIOAS5Axw3FcuAn0TZI14yxH0vkY8eeTJ7s/jQU8Oh+zp8Qxd2XoC1wXuGEx9E+RYSoM9mCa6V2qwVIYUe+staW7mXbUhpT5wsk8RxKcHTmQYCXz2na/wE28aRsoI0vzDH/CKL/39c3ecUwiyQpEEHniXLauIIi3Uaay8vPRji//wsS99M4g6ZSTpbTzhbSFJCHkiH3hOzNxAniqaBH/2z+Jv8Hz57X/3f5+TnFMIggXed46k43v99tLH/y2Gvft3f+1T45PhjJzfHPmJD5/xeoyvPpqZ+9qrxpj/GVn/5Z/N/UMQ03/iP//Yj50RCl4NOMRa+pWXxrjpt23tZvLMqwO+x7/d/WF86uV+4cWfPLcLO1u8Qd6ikm3ly9PAD+w3hP/cGUScQ3FRyQaJfuzpz3gOpT3wD2TCvXJuYFmOJoBKts/j74bxAPWb1ieO5nDk94AE+dwfTL7hnff/0cuy3WSfeYx/x4/87aPfhB/6695R/9m3WU/83JH8Tv2p/MmHfu9p89TXfbxAJdtXsdfauYs/2H6mDylOjoJKNgy7/qu/+EMn59H3U3TC9S3CqRFRyZbDZuw8ZrH/Tz81r+6LAYdYb/olGpL5yuMf+KSd2pc+8XYM+8Lr33ITwz7wN+/8XRyzfv4zP/Wj3Xz2/rwf37vFHnvl/n0OJnPWzGdesf9k0DtUst3EF7qiXPnyLWTjBVSyYcYbp//9oEAdjY9MuKMJI/iNSrYIdseWZgXTV3Zq91TZrD6vVfzN+zEfnIP7p/GF7pPHiK9bZjhl2D9qxE9b1qGg+I66qvv3N7rhu3+e/Gqh8/23E8Jz+4/6vzuU4cCy/a/Ef+yK8mvEb/Uv0l5MtLJZ76P+3rLevyvhXib93iAWDsR4Gn9jv8IciYdWtj/Br1QsS/ufceKZI/kc+TlgD4I99cfPVg0MY7sEfLT797Gvf+fRW5WF37R/WPz17rP9P6eMouwp+n2fEj7y/s/uBx30DpVsg8pxUnxEsv39h375ZSclP9gzRMINJsQpsRHJ9jNPfem+13NfyU1unDPJGJAg7/pY6vE0j31mvVucePdvwqpjZWzpyZ0SSqcU9MTHv/CRr5/4vK+HqGTzY/Vu/nUs0JccJ0VCJJvxpkUbZsSaEETCnVTugZ+hko34wu889Z+4V33uJ7HY2TINRpDixx/8lq1C+dOdTPLdf3K4H/NjP/4XJ2d82hykGzqKXYhPJ+ew+xSZbIvWrW6SS9g5o9UzxTn0EpVsrSW823fjb33rL/7OoRwG+IFKuAFEODUqOtnIf/fvIBdlKTJ1ambdF4MRZMV8jc2PzZWdTL7R/eer2CPY1cAzBrnz8MjfJw9M0vEDk/RusL8DtQKqC5lsr8L+xpap9U3PDzhNNvatXYn+x3ceXXw5KtkwZMAhk2g/IdSy/Zn6hv3ET747Mifp+Wd3zpTDfwDm4s3HcQLiPY0Tn4B/Po8vwt9fx9/esdPKPn9kkm4/PH5dl+xnd+aRzIN3tALIZLP+NfFxEO5d+L89LvfFn6DFbSd/tJN0dMCBdIgn6ehka9jQfScSye5AeOrfwXqQ+M/8+cOvrX+Zf/jZLvse/+W/fmjpL/lPw4/3Pff7X/jhdGHpmx+8djIxDz/984+8Ysp7+4vK6959+PkAv5DJhn3qB/+3/37tmaevfmAAaQ5HRSfbTrpIJyHohPurz2M57FtvxiK/fbj4/f9CJ9tr+Ae8178ofCFxnjCnUuecF6vEWyBE571X+MknKo+RcP808eQzr/HDSvpOzKdeHWYzj/7WJvQg3aBnpve1N1wLMrHXPnVmoJ5fopXNsjbfkmKnf6nWc/5nBUQtm50Xuh4EaaWCXDvX7FmA9PoOMXAf/hdBbu6JrXNzx5E2Puex0X3vInDJECAumbyuuC4CI0XAJchI4XYzu2wIuAS5bDXmyjtSBFyCjBRuN7PLhoBLkMtWY668I0XAJchI4XYzu2wIDLBQeMBmBF2pEWmdnSwb5mThnCzbUIA774NzexB05HZTugcRcAlyD1aqWyR0CLgEQYelm9I9iIBLkHuwUt0ioUPAJQg6LN2U7kEEXILcg5XqFgkdAi5B0GHppnQPIuAS5B6sVLdI6BBwCYIOSzelexCBAVbS7xU0CJriPB4PxxMdud1ud3QNHH25l4tAFwGXIBjJeQKxaDQcpsrlUqFQaVsuQVx23EXAJQhGcL7YzOzU5AS7vrF+mzQsVbmLjvvvCx6BoRMEx3enObSHZ+AHRZqNpur1ChzHWJghwdVCd6zAheuToKjgxGQmnU7GQ4xKsHwwcucOOu91F5bnnAg4w/B+P1up1g39FCeu56QwjNeUIAiiyIFPfxBMU8/xlz4MCYaX5rAJghMkuWMh6olFvQRBcZy2vi5l0slQyGuaai6f286OkyCcJ774wHQw6BcZ0kd4AukkUc4ND+8BUyY93vDMdODmLUOR1QHTQhedDieSqWQIw9s3b622mi5BeocWugyS3ulChNhMlCQZr6AwbSJz/0ImHTGNztLSEtYs9p4g6pAk540vvHSOZWmCwH1CJKWky3B6g1MvwhNMv+jFCVEuYZpzZKRD0wtXr6ZxrP4Nq4kpY2zv0GOCvgfB7S4DJ2maZmiSIFhBsI8QgQNoUqkgQVIs26l3agvzM9GwT1VVS1e0sY0VQH8VTKWvzMYDJIVblmnhJM9KAb8XFFnn7RNAUxUUYxNTU5Qes4P5UlBgAFaSGMrOjT5KRXJ8dPrKwmQsiOO4yFK7A4Y+UnJkFPQEIYEXOM6J9rCUoUgxFIbBKVysz8935yPtKVyam0nxlNpuV8ulUmtsYwWS9yQWr85N+SkCt0zDtCzMZrQ/2G4r1khoy4giTVGtWq+aZZz1+ki51nbQKIbyhzNzV2ahlq2um/keqe5INpwgFHqCEDRPEYQYCoZCYQ9NB1LJnaNr7VYPw2HsLFkebSITgeNLOo1qpVRujo0gFO9PLrxk1uu1UTANwzRxmmGAIISumSNxqcd4wyzDlNVGj5pl6EF8ZMdoO2l85U/OzC3MMIwFnTA0MffYhZggJEWGEnGOJD1+n9/v4ynKG4nunTEJ6Mm1UrXeMApmVdOVZrNye6s0FoLA5IimApnUldlUiKFhuGJoSkeSrFCYTdyH5fOlljSKoTTjjXg4Dm/0atAArY/IwRDLKeMrYAPBin6f6CEIzHKAVBSs+nIsQ9MUaX/arZYEIwMY7iuyrGv99LuICULx/MS1qz6KYjiO5ziaIFnxUBZqaSULi9VFgVFVpd1ubmdLnTH0IHAQHOPhUwsLs5MBlrIr1lLbtXxen+P8GTx6Z3U9lxsNQcI+3qPmL0AQQfAQzMlHS4yl8cZpjrdnHg5gB5SfEQQYuoigdOZhZG9tb25rmiaIQrVSaUuOIIgv86J/FaFh6gkXTDlwHAZW+5cFBLmjaQZBWsBpVZFr9YY5ktH+vgzdO5yAoVRq4SVTPi/brVtTa9c2bqtcIpaJzUa9tNWoHIkyjJ/QgwREsSUcxOiMfHCc4XYI4pDvEWS1CQKqhjOkHuUrRgymM5lwOOz1eoEgzz//vCwroVBwaxMjtHYfkhxq3vuIfySKZZoE5w2wh5o4Q9cNyyRoGnzAq9WtFRjsw5AfVFi6rrY7YxlOcwF/KByZupKGKcBO3eIUy1iK1Ky3aIEKR0s55kjRhvKTC6VFGAD0qPkhSd4fi3N4By51HM3KMQwImgnF0qkAYxmG2mjkl/MtuZ92+ljCfT1gWA7WY+AKBAJMVz+Y6BCKovoDPp6htpRaH6kiJoihdTqyqh9O1ei0FcOgRJE1LaWWXQd9KpAbWAPz4r7GhX2U80gUIT2VTiTiieDO+ArekizWEZiOUq+IOM4KMH06EmUoP/nwJKX12kTgFO0Jpyc6Heh2W85YryYFbzIzMxXhwD6nvnLnzupadYwaNk8wMDc/FwwGeQ+vtlUKRvopn27oPM8xDGvuHDV5wXpE/BmYKt6RFQ2MSA5cRqcuaTpjMZRlqkCQnUMnbY3H2NQenvQD85mMn2H2xs4ExygCiyv1MsbSzOgIMmHWem3YcJIRwpmJbKveaDSdoS+ihFBiYmaK52CEWrr5j883G5LeK98PfCGIbj3h1OKLXsTzPEmQlU4F9INcioNvzF6OY7n6Zj/ZoCaIjjUKa7rPT+gG3V0iNHW9trFZ1TQmEvGxbKk+TsurHYTA/ioQn5xKxARbeaVpqoYLHljNtDS5WYYZCRidUL2OevrB/EAckvFoXR3BgWen3tLB6EwqyGNys632qBY+NS0ULwiKDmQm5icjXorUGoX1tfVNVdXGMqWE4lAUHZ+dvzIZxzGj1ekUi0WGYeKJBCxYw1u2X7UGYoJYulXf8NfDIRgne9Np+OoMuZO7+f2cptOwMhIJt2pj0Fkd+RpA1RaKJsKCXXawlmw2GlQqSel6u9WoEKrKx3xD8eF3RIqdn2B0cOLzkx7S8StXJ0RT60iKE/iBkR4hMXd1fsJPE7heXV/errZhejmerg2GxR7PxNWH4jEWBvn5fL5YKgFlFhZwr9cmiCpVpb6+PMQEMS2jtknWEnGz0YjSEbuJlpv5W/+wpuu03x+bnrbqfYl50vfS9zOY6oaj8QjXJUi7WsoXGCIoGkqnVa9oLTXWsT/a3r/bvuWAiBfhB8bErz44IcL6qqSMbyJ8oLSkJ5Cce3DO54U+UKtu3N6u2ue2HggwuluwcmG9gcmrL+Z5RpIqK0tLpVKJJMkm7jNoUGdhGhCkr7EfYoLA9KJTwtqNCti0171hjGXVWm59M5uDSXqr1lQ7RK4fXRtCpMEcLJxKL6aDHoqA8VV1c7NQq/N+L3QkW6uFSqdhVhWCEQM+r0cfskU5mP5zLG31rCElwRZfpC25UWn1VdcIUewmRXlBgZWMMQymafX8+lpRGlvHxnB8IplcnIwaZjO3uXFnZaVeq8NSnEmAFSpY2ZnN0nalr4Ut1ASBs9lreruaM9udTiBghEJKaXW12IGe11BA0AaeHzNBKI5PXrtvdkqE9Rm91dy6cbNkYd4tgisUsmtbtRZN1FXcE1aiIX+nM9ydU7CAANtirB51vPBNkmCoQJjtanEsxgfH+EX7U+mIl6UIs9MqZDc3G8OF61j+Bx4IodD8/PxEnJQlaeX6zUKh2JFNLhhOJuMezp5oVnNrxb6+PPQEUetSlWUtWCgPBCjSq5RXV0ttMI41FU1ubOHKmIdYJO9LXfuBjCja44Jmcf36tyt+n050rPX1fKMhwxJ7XSE8lBkN+8HK9kAdoL+lPV6vhzN6nj7CbjNYsTY6jiFIIJWJejkc1zu1IhBEH9/AzxPJ3PfQQxxHqo3S7ee+3WlDk2yyoVQyESdJzFA7QBCpr31w6AlimmDVRMLCUb1eb8omTlIUmOnA52Wa+nA/uPM/YVjWB9uwiYl0lCAAwWZ5e2NruyorqlTRt7cqmq0c6qgGThPecDKTU/uC9HwxdkPYy+gCI/doWEWAIaVtvKOpUrMztrHMfuEInA+lMyGOsEylur2Wq/bVQO+n1/8dx3umZ2evTCcVpbW5tbW0loU1NkhOjM+lQemHWa1yeasAitR+skBPEJDCsjsMy4JVQNNigplmeSSr0j0Un2TZUHpyKsyTpgbSVfOb2xVZaWgtljVhhrS3CQRWHIKZutnoIckBgrD+eEgAS/uekoCGBj4ED0PADhq5V+v4nlLuLxBs+xHC6bSfAWPAdmFluTA2fmD+eGL+ynwiiEnl8srtlc2CZq9EY5g3fTVtT9Cx2urtlaJi229c/BoOQUAXDquAsATSJUhjnRmJ7fj5pSdZqNS5qQgP83NVUSq5ja1qR9FbsJIEE01jTwVDWHRwQm5sn5/iICHYQMImSG+TdBzsP22CkJiuyuPbY7ZXXmhDPOFMhoKxgt4u3FnO9zUF3ktukBvfxMLi1UWWxVq59evXr7dAhdEliC99NeSzE66tPrtagilwP5kMhSA78qn1fDAcE1hYcghUNAfUKYbz4ej0zEzKTym1Wqvdzq+Csb1mnDBewUlPMNpVoPcDao9xGCHogxGKJu93XafHpLzBdNTHEoYMxkVDVq+dLsXuG5ip8YHgXCokgBW01Mpub27VxjR85jhucv7aTDqia/Xc6s3l1W1dt0B9TlN0KBoXWEyWO1ury8VGX/0HrD+eC0bfAeQCTgjBGO6LxmL15vj2De4VgMC9mbmZmamwYDXWVkvNZq1SqZ8ynAfbRf6QxeVeKuhuKE7kKZjhwsL4+Y0bDftaJwKMIUsd4Mf54dGJeUJKYKMYnplZnIJ1LgwGqisb2fK4TLC8sdi1a1fDotluNtdufTdftcdXsNvHI3ojfg9DYY1iYXl9uyWfUIxeHg2RIJ18Ew8leb8vGo1VrPE74QDUvOlrs+m0B7wLrD+3Uat1dFjnP6H/AOBGQxAvT2O63JR6IAgTnb9vIkDrcgvGhHuTpV6qeAhhwMQ9PP/IQtImiFbbuLOeK5/UDw8h52NJ+jLz165epWhb+71687udjg5DKXAV4glFgCAUiTW3bt9eg27lWMzeHgyRIFqrI65HScznjc/oXqEBprtg4d6bWMMIBX4kAomZyUjAqDdWlpc2Gg0NVhVOs9oAbyy9L1D0Jy5Y54LaVm/XzyII0BpMJhg6Njs3HRUwCRa82mMnCOXxRdOzk17G1LVabm0lV+u3ge4Pub1YOOFPL0wnQ6razq+tL28UQDcJW7thw1Q8FpsKQYdr5FdvwBLmXoyL3gyRIJiJtTYZnQwxift8hUKt02lVKmMkCMULMCoNC2Rt9c7yrc2SLBs4YWpjHq2AEUStqZw6QIbRPiuKsH05sTiXDLB6dW1puzEqnyunfkyUGIlHAl6ONKRWbmNltTQmfoDTNX9yLi5gcqNx+/nrW0VYkMYohpmYnIhEIukIITWbq7dv5punluTcF0MkCNjlNDfaZmgmmuAT4PO23ijh7f6pfG5RzgsAzR7YKIYoorX67eWt7aYJAxXb2c958Yb8XmtXm2d4/QFXe4FILJlKZTJRhmlX1m5t17X+FDLoCgIEiYWDXpJUwGhi886aNKYZOkEy/uSsV7DkWvH2s//U6dhjT3BFPvHgg6FQiOcJuZpfu31rkK9uiASB9RC5KovrGwQFGyCD9XojT2gWeKYc0zfJhcAywscacnV7eaV6llXxSJXSMErZU0rB4gJMf8BGi8RheQT8SoDzAUoIh0HNEYsFBYI0mgWwKRovp8HbRSgxPRkVKVNp5DZvr2fLfY/wB+Qs4OXxR8GevZVbubOZZegwDEdFrwjTdp/fXgLplNa3c8VB5kdDJQjsq8XKd/ytRJzHuFCnE2dIT63eGrYR4Cmoi5lrcxFal9qwVaDWPv0jg+3eODjo6G397pS8LvCYoBgwNdi5KM7et8CKAkuBCTkOCg4enFF6BB4MAsHiDpx3yc2xO8Wivd6J+WtzMdbodPK3byyvNcamVYMJGknb8MFKx4bEwU5br1f0eX0Tk0FQf8DVLq0XWvogW1SGTRC9vErIWCjIw8K6USJxehvTlUEE3v2U+vhHSN8/G6HB7hksoatnNiqj5AcG246ADDvlAUeoYCIGvvZEloMldl8y6YWuhAD7q3aXIIShNOudMc7jbDFpX2xy/r5JgTPa9ezyc8u1uq04GssFMzSKtp1u1Fb/uSWxwXQ6Gov6oPPwinZfjFlAkCL4Ch5AvOESBEjRzJkWJ1qCAG0h19EpjiSazfbI9w10HXZNJnxku1kqVptnTirtr9Uy1OH7RAUULIsNJHWS2THhFPwBGsNhTCWy0JVYYizuAQs2VevUqjjj9RKmqbQaYyUItNliYnZhJgN+lmW5CbYIm+rpKoZRsKbbtpAMzwbNdDoDg1FwNW+PUXHY0G22ipvl9gD0GOpC4Q46WgMj8NZkEnbweZiIAdYSYi4HRignLz8MDVHGK8K0UmRxtZEvnbNvAdpDy1Ja9WE7XOl6O8V8k1aqVN7pFTwBP5itgTtj2PQLbl9wOWfBQnCr1Wo2PYYvRBCm2m6dppgeGnYHE4ZpUWTukfmMF0xkYG9j23bRMdAHeDDxC9/DsFNXFaBD4hEBXIEEgwEvbN8iNYO0DZ/hK2uWs2c3hudmOdweBLLXGp2OlNtcBPsYho2KITjrjJDbijligrA+2EMYEGkcDGBKrTMzh/q2jfOl+rAXh22CmJh3MtBoNnckAoJAD2KfGAEMleA4uGqr0axVK41OO+yfMU1wetFujRq6Q98QwXCRuRfPenhoou29jW15RF6MD0lx9wf0EbC4Bj7Hk8I8dMYMy8B4Vdc1e4MhDgRRgCD9GfHezWGYpiY7eZhgOiRXWxautuQgG+Btn3EGVq32ZXu8J/aFb0DH64O5L2Z2qtni6QQhGQbsBy3dkKo5sIO/cDYXiqA0CjClpOigxy/v6Ax4GEYZugE+pjQVOo5mDbYM1GuVqmRa4JEXB/todcz7aWgxkMhMpbrl1KVSsSGf2dhcCI+LBwZ+tIrr4ONW9OKmaYIDNq2tyR2Z53icgn3oLXCQdPFUD8UYeg8CuRkKXmTkPLihSiY5vwmKy8CKPuIFEfDwSMIeQsNslbcLpx3xAu1OJDKT9JgdCdYbnt9uHYIK+Y/6mgYegD0sY7eC3dShftUW2JKAm+B2B1xPwrZM+F+STFHMwG5b2Pgzbn9xXHRiOrRzngWmllbvjGuJcKcyTN3K32Cmpzl7I5mqNltNaFAUWUmnUwQDKqxieWAb41EQxFR0XSluJJLThk8MwDbToF8rIP/czk4Qxi3gCxWm3jZBTutBcIKNzoCFkdkB+4mb15vDJohWaLblkN8HrZ/t+NnClDbZLJTqktSo1WXoSMDpmf2fBsc0TERtgoDh79nlHPZbLjo7FaJ36KyU7sA2i2HneFb6lmHmr7c6fArGpboMhChuZ7ehk32R4Wc8WKe8UR54l8ooCAJOFLVOpVyrqKFpguc9lIfKrrD2+HuEF6hfQLFh6J1GuXrKNBdcKIQnrizEWbVR2NzY3DKGrE+V5QpsV40Eggc33dbz+VpLalTrB788zhNNBj0wSgX7yhFidjQr0KqK0Yl0wP5oTN1olLaz/fnSOZpwv79BnVLDmh6vD9y5S22pkM9vQrWZZmwOZiGYXC8cQrGvXEZBEBAMNk91qoanBjYVOGwlAEOZMPgj7UviQSLBpA62G528/aI7vkovLs4GiVZh7Xa+aYzAZlbK6lnw1n6gTO1Gow1DrMNDe1Kw/ZnAeDt3lgHAgVSGcwstCOzwgUkaJK+1WrlyXRqvihfkUJrmLXUbtPiKqjQacOBMg2FZfyjqYWFoj8A/0qgIArQ2WnzV5gTJkwocS14bskuEE78RIIimqCdup4DxVXh67upiBidg4nc71xzF8pdk1MCYZHeZsCuxDrY43cHVQfnBeDssUkCQ7OBN4sF0L3hPcd5gJBby2D2e3izlSnXwFHfBNFAHV01ZzX7XXky1FVpwmJ3qF8RAKAKo6jICD3sjIQgcLmVPkeE8BHuNGvxEwwWnwKDG6vz0DKWruD8eEFZkWTY8OX9lIupp1PN37oCTmFGMABWlJ4/KcC6cyJJGq5Abq989NhSfTEb9sKPfhCW49bVCc/SDgKN1Z0A/cfhUYhFO1hO8sJQFrjgG36Y3CoIAIXivCCcpZWa8wApwBN6olcvjcH6mNoonrhKChGI4lJyfSwlSc21tY3PTIa6nDn0ORquUG9fO1q4g3tn7r8wGOBq3/ckuX1/aGngKfKh8iH7Y+hhYRsesTgmW0QdNdCQE4YRgNBby+uLTXuhATFWqV8vgY2dQ2S8e314lPGkZnWC94ampyempYEsqPPvccktqD7i+dHHZzo9hz0HG2oP4Zl82GwxyBBxF08guP7tSG1iJen6hLx4CCNLlB9YubzQGlnDIBCFst1hwbEM0lYzCmTW2s15wot4B04nmIBZkF0dtJ4YJDhKOWUbgNOUJh1Jzc5OxGF3OLT//7HK/6Q83nik3xmvKyycWpsGy0p6hF7fXb68NW83XH5ygh9kxL1YaJWXgMeBwCQK2yALwIgpKq7Dfw4te+5ABcCyngGOnEVvwgNoPDjDzJ7LCQaWqXQUM8BdWlhLxAFGVbt1aXh1g/1l/VXpZYoFt+c5Za2plY6MMzgsdKbhcMxGqMoZLEIL1ROxvLxEWvXDiLex8AF7YmtZd114jBRhGd2wAi4jHCBLKTM/NpcDKo1rdgu6j4RLklHoBX1hgKgYvlcrGegm27424kTtFrCOPZbNTR2ckNDSC2HtZCDC+S87MTCQSAY6FnMD4UtU0qZQf1xSYFslI0G87PYCqBa0a6NZwIjg1C46PYyTRVgqrt2+tHMHbOT8B0TEeAA29h9f2W2eTQqttZ2u79mPOwWdXEkPRT1kJ7kfUYREE6hI8E4XiMbC/ivjBBbjd8BiKIlUqpVxua6Un7WY/BTo9js0Jig1OPhBoNMCamGI5AZxHM7AfIxkP07Km1VZX18ZrW3S68LATF0aIHqF5tP87KwrSd+An5FqCBT094Gh0arX2uFdATisd5xEC7GkvL/x8WAQBT5neeCw1NRkLBkQ42L27Zw42jJZBiZrNFgc2srxwQe0+wyIoPDjVhA0pLU1jfd5wIh4UBV8g4IHdP81mcW1lrYyuc76wiOdFgKPdYTXkvFDDei9OLNoEweyVLKMNTsWcShDWH/bDGemIriEQxPY2gHOCGM1kpmamI3z3+D/w+W4Y7Uolu7S0msv3emwlokLuJgNmLhTpzxhwdmJdVblQKD6Rifh9HjisQZLqxdL2ndWt7lo/2myRpQbO7LjdQ6uRpdlrQrD/N21v6YfwcDYxnJbdPGwM02s6IwjH+sA1BzK/G+gJAoMrGnZVwLgFfHdFYVnJXjEHj7JStV4pFgu5XLExhhWQ3YrBMT6CeULplqqxgUAwFPLyPKUpcr1R3NjcLhbGq0Y99+sBVdx4LjhEPhiOBj1w0r3VadTXCi3lmLp8PJIdz5XiBIT97BAIQjGwr3ZicSEZDMLnBwctQxmMTrO0sbGdy1Ukqa2OjyDgvzoihFMNmHEwPjjeiWMoCkx26pXy9tLyNtihjMuBzfF6PvrEssc2I/O1ciR3ihdtKyweDgOx5OL2ahHOabdn6068KBacwiATDF1KtkiwKwmGAbDe4Zu970WJ3WUPMIw22uXK1vLyWi5XH/X6xy5S9lkM4G4ITHbZgL0OA6fuCiIonW3FGoyuSqXNWzcPm/QggxhdQuDC467/E3SJ9pQSyYn+IAztgSCmVFi5k2+N3bvjqXLj4DQWnZ0fSoLA+U2BaMQrCKLXG58MCzCjA6tPGdwf1CulUiHbHVyNqd0Bbz/lShXOk4dJLkFhJG2ScGiJaSitVmltPd9sVQoDWyWcWmWIXhCcLyDs7uZDlGSvydjup2DHN4mboOzbuL6cc+oiCBRIbZbAiBLVJAQlQQDF8NyVMGz+FoEi9umOIK7RaRQ3N7OFQrXZkpQe3Jj3WmcXC6e1yEqlwmD2WVcwVact28cx7OSSisX1Gzeymir37SD/YoIMEJrg/XA27wAJ9B8VLIYY0BCQsMor1zZvrLRGYuvcn7xKg22qXXU0ihkbIrzBuxn4SWXY1MJDCZ9fFOx6hL4C3HZIldLG8u2NQmEkG5BOhVRrW+VijjRJzt57awezPcbAnr7NreXvftfxg6tuucDo3ccjqrBTgTr5BbhDhS6EBiWvrjZzK2snh3LGU6VJdL2Bw8IRgnVVNHjDJi5wQC4Inom5qQDv4Xa2AJmg8GgUtra2s7lSo88j4lBhbmpY4Ra2nUiEAoGdExOVRr1WrZbyhWzW8YMrG4WxabBQVcGo0tE7WKPZAr9YoM4CN54DZouGILTgTWYyQZ8/HI3wFA0GHCAWOBttF7bXVlZy4ErR3sY3pulHFyFTNYp4fXuyMmGJuwQpbW9vbReq4BnGwYuDB6t3dP6CD+Z66e7BQToQBI6EBnWWPnDVoiEII4ZBrxsNBj08KDrghFtbBwjHt5c2VpZu3SoZ41cJwoCqImWLtYZKeY3uDK6yvbJy506xJY3YQ1c/HxxoE+yTL8Z42RIYJo5OOzS8suhwZle96iNYWggaMjFgu4yGIGwwmU6lA4IHbK7A6Rn4cwKHwe0qHC+fy+bbDjGLNnSltqU2tm9ysJpgYc18HjQHbSccLnrux2JI5WJQQ2c/cW6GRwPoCngiqrDcGEU4KtJZvxtbN1Npr3/ygXUSNPoDtYBoCMIFkql0SoTTLAjYKg+ulisKZlU2N8t16OzaDjGLBmOXqlLZFGH5wyaICn5i2u0xT43OquUD72yCJEADOLZOxJBbsAvUY41HyXwAid5um1s3MB/mm9BIpda2HEAQWgwFA34woQSvTe1Gs5jLdSwrv7TsjNOfd0G1dEwd1BFlb/WDPJTeKuXTTb6jjmt5zlCwejnPmSTWgTNEx8bTXoFtbtPepCmmWaWeJ2DSPsCFpgdpbmKV9e/tNC/g06lRA/dNVi0vjf20yQGQcVJUvZ7FtIKvWMxuDNnb4ymlBiVg8aa+5BUxRdlYGdgTwim5IHvcqVDpfF5no5mmuomBZ+P+KY2KII11cXcvK1h0KIpt6imD54MBJEOG1j2QkF7XmoWbTLvdqo+JILpZMgpw0BWuG/XCiP0qX7wCOxVtO1fw8H5YMcRbOXwAF4BoCCJJ2YuXwo3RMwJ6s5nvOfAwAsLwtFweRsLDSbOjt7Lb2bgYSFpsK0vtnQHZR25oCNJHxm4UF4GhIQAHbBRuWlOTLY1NrwY8Ctb/5i6XIEOrJTfhsSEAJw0VrGpF0v2BaNQP6+n9S+ISpH/s3JhORQB2MRRrq5JOzIKvbR+vDbCe7hLEqZXsyjUQAjDKKq9o20ux7y4NdOypS5CBqsGN7FQETMwsa2VBFPL5hj7AUuEA+0pQWNsfg7d/hfWhpJws23C2zbrAHfoAev5xHm6Xwfqs58K6AV0EUCMwQA+CWhQ3PRcB5yHg9iDOqxNXIgch4BLEQZXhiuI8BFyCOK9OXIkchIBLEAdVhiuK8xBwCeK8OnElchACLkEcVBmuKM5DwCWI8+rElchBCLgEcVBluKI4DwGXIM6rE1ciByHgEsRBleGK4jwEXII4r05ciRyEgEsQB1WGK4rzEHAJ4rw6cSVyEAIuQRxUGa4ozkPAJYjz6sSVyEEIuARxUGW4ojgPAZcgzqsTVyIHIeASxEGV4YriPARcgjivTlyJHISASxAHVYYrivMQcAnivDpxJXIQAi5BHFQZrijOQ8AliPPqxJXIQQi4BHFQZbiiOA8BlyDOqxNXIgch4BLEQZXhiuI8BFyCOK9OXIkchIBLEAdVhiuK8xBwCeK8OnElchACLkEcVBmuKM5DwCWI8+rElchBCLgEcVBluKI4DwGXIM6rE1ciByHgEsRBleGK4jwEXII4r05ciRyEgEsQB1WGK4rzEHAJ4rw6cSVyEAIuQRxUGa4ozkOgb4KsEW85UpqvEU8eebL783jQk8Mhe3o8Q1e2nsB1gTsGU98EOZbSQA8qf/QTVzyBRz9tDZTKkCJ/9p2v8BNvGlLiAyb7nldPesIvfrIyYDLDie5k4OwSP0UQnz6v5NR5AUbz/v99e+pVk/nPvfW//T+jye9CuXzgOTFz40IxRhf4oy95bUx65v1/+Ex6dHn2nJOTgYNCbDzhbZ1bFocQZPELrwNRP/jSz/7lj58r8sgDfDQz97VXjTzX3jJsMna4937wQ5/oLfxIQzkZOADizZGf+PC5eAw4xFr6lZfGuOm3be3m88yrA77Hv939YXzq5X7hxZ/sccz0mM0PLPYL1tPdyEj+oJINe+UcEnkOJoJMti4/sH+DLR1MfcB7ZMI5GTgM+9jTn/Gcj9SABPncH0y+4Z33/9HLst2cnnmMf8eP/O2j34Qf+uveUf/Zt1lP/Nz5IhwIQWMIezTEsh0Qc/BbxLL9F+yhwWXaSwGxcHvporhBJ9v1X/3FH+pBogE/yDf9Eg2ZfOXxD3zSzutLn3g7hn3h9W+5iWEf+Jt3/i6OWT//mZ/60cNSvB/f//3YK/fv7Tvjj/HHDz8Z5Bda2QaR5HhchLJ9WKr/0zcefs/xPPp+glC4vmU4LSIy2Yw3Tv/70zI59Nzq81rF37wf88E5uH8aX+g+eYz4umWGU4b9o0b8tGUdCooT+9dvdMPv/3k3/qP7Pwa4O5QhEtmext84gDwHoyKXLQF4/kjhYBb93yMXDj4KhwL3PurvLev9xH88D6wBexDsqT9+tmpgGNsl3aPdv499/TuP3qos/Kb9w+Kvd5/t/zH3b4/e/d7v3Pefjj4b5DdK2QaR46S46GTLYsVvvefhLz58Ui59PkMnXJ8CnBENkWx//6FfftkZuey/GpAg7/pY6vE0j31mvZtivPs3YdWxMrb05E4m0n5e59x94hcf+ErgnDAXeY1Stovk20tYpLJFX//Iwpue6yXb3sIgFa63LHsOhUg2402L9vfZgwppMIIUP/7gt2xNwJ/uFDDf/SeH+zE/9uN/sfPs6N9T5yAf/aUHvxI5GnqA3yhlG0CME6Oilm3yvmcroRNz6uMhauH6EOHUKKhkay3h3UEP/ta3/uLvnJqb/WIwgqyYr7H5sbmyk8c3uv98FXsEuxp4xiB3Hh75++SBSTp+YJL+H371xV8OHgk70E+Esg0kx0mRkcu2jZ2M9kmZn/cMuXDnZXiB96hkY9/azfR/fOfRxZefk/15k5TT3nfnczn8B2Au3nwcJyDY0zjxCfjn8/gi/P11/O0dO2r2+SOTdPvhCdeT+MuqJzzu7xFi2UAIxHNNZLjdqtsImf8H/mh/SB2J9cIBbqfgQ5+kx3/mzx9+bf3L/MPPdmn4+C//9UNLf8nb9i3ve+73v/DD6cLSNz947RyGdl//8f9J/eDH7Lvp/6WX4L2EQSYb9lefx3LYt96MRX67l3x7CYNMtv/6qz80E85/bSX1B71k21sYZMI5GbhdKHqYhBxpQ3r+uUq8BcJ23nuFn3yi8hgJ908TTz7zGj+spO+k8dSrw2zm0d/ahB6kG/TMlN9/V/f7qjOD9fgSrWygDNy5ZnvM/sxgaGX73hOPROnAy55E1PuiFc7JwO3UUQ89CN4Dh3preNxQLgL3IALEPVgmt0guAsgQcAmCDEo3oXsRAZcg92KtumVChoBLEGRQugndiwi4BLkXa9UtEzIEXIIgg9JN6F5EYABTkwM2I+iQQaR1drJsmJOFc7JsQwHuvA/O7UHQkdtN6R5EwCXIPVipbpHQIeASBB2Wbkr3IAIuQe7BSnWLhA4BlyDosHRTugcRcAlyD1aqWyR0CLgEQYelm9I9iIBLkHuwUt0ioUPAJQg6LN2U7kEEBlhJv+RocF6f6PGQui5Va5JhnLeieslL64pvIxAKBcHdZ6VS7R2OFy5B+ORkMhZjOp3C8pKpWOD9zr3udQQiiwtgS3PzpkuQHmqaT15dnJ3lm40VqtawdJcgPWB22YNE7/tXQBCifKv3goyqB8FxAsdJlqVxwmxLSi8+7XovxMVD4iTpi01OJkIsw7VCfsFQtIsnMqwYBEnhOEYyDO/z7vh03cvJUJROvd4a83gQJ2maF0WzUunsSeb8G6/XOzeVluUOgNv7NSKC4AQJ/3F+n0DRWj6vWWNmCMEwvmgm6aMIBgPgBKV3D6m9Y9tvSIJlgSGsV4xOTvoPc0Gr1Yt3VtrWGR6O+82193g4TgtiOJ3SbqqXiSD+iYmFTLjSbsl672Ud0LNi7xlBq0hRQjgeZFgZazXMw/XeezqIQpLQOkfTCY4iWNrn84oSOseEg0tIsgKDE0I4PPPgg/HDLYmcy61ilbw5Vmc0QBAxlLnvWkfdLA5e2pGlEJh+YD4TbpsOIwgMZhjBw3Mcx7BCOOSl6Q5Nco1Ge2TAnJSRZRq60pFJmgT5oG+7SKd7UnqInhEUxfCcNxhkccITCqanY6HDfa2K0UStaTYaEjh2QpTpRZOB0YBvcnZ6KlP1eWhz3E1dj9ILonf+ypWkj9SaxYbSYyQ72NCHWATN+BPxEAxjPB74nyVJieWEDWPMBNG1dqMSwln7/B/nXCTH+cPhSCzqwXFQQwej3GF+YKSAYQ2N2dhUjLF9mThJ+edeOu3zqjzHajBavgyXP52+dmXWK5hy3WkEYfjQ5Hw6GAp4fTwBM3VM4njWqIy3czZ1vNMoh1mfsyqX4rzRyUwmnRIxnOE9LEUd+fwIgec1kidbpcNDr1EWAyfowOzLpnWj7uEYyxjrdKjncvsn71tYmAUNUadeqDukByFIkuV5b8Afn5yM+3wix1OWZY9ooopS3OD1cUJrmboqS5LqsNrloskM6NaiUR7DKYY53r0DfHxYtyrZnNwZm2Ia1JG816dqvN3SqT1/o2MNyAeTEb9HUZRKfutCurfjdYCsHATL+WNR+C8SCns5jsY01TQIjiXFmLwWFGV5jF8nqIE0mIM4bXzgSS5OpzNBUYSRH7QkJ1YF6cWszbAP18b7aRIUznIsq54s5ImSj/Mh4w2JLK5JjUJ2s3WR4f0QCQLKmOj0LIwYQHNFEYSpyJquExhNeSk9FhAsfZxLD6apyW1ZGyNHT/pchOTV2VRSIEhQGuCn+CggvR7mTtinXaSWT8prwGc4RdmKF8oh6o3zSsOIYS9LaK1KMbtpXKTvHQ5BCJwVBF8wmJicSMbjIkFgpiEVCg3D5DJkgMFhxs4pY217CBjCcLDccB6wo31vGTpG8TycDWYTWDaAI91JCEHRFE3v1JX9ZbI0dUr/MjJ57aVMW/3nMAQBJo+Hbzab5gEtH8uykZCfw+TK5vpmRb4QRkMhCCgC/SlgRjQSDvmBuARuqEpp6VbesoK6L0hiDMuOt+0BCVkBpkVOWv6AetNapUjEbt9MXW+VKx3767MZQgmCIIpDqStI/V66WFGMx+Nrq4p+oJ/gg6FE2McYrfzt62v1ixV3KKDDqqB/4tpUJu2HqUd3LG2q7dLSP6zieMo/Cy0Py8AMdJw9CBhLsB4w5BinDCdUlNosVdo2QSxdbeY2mt0eBBjChoJBGPKfEGOsjxzWe9hYsL7Q7JU5Qc5iB8xPPZF0IuzXDSV/+7mSEwgCDV5ian4qFvd0RwKmaTTz+dtLa9sU5WnrMLi2D6QZa+cMPQjDeXjGJgjOwAINN8aFhf2PXKlsspQOumddVatbW827PQgTCMSmKeiKQV4TXtWbknIRe4n9HNDegYEd2gQHTs2XmZ6dmymsCYS1P8X1Jq9kQnylXFndzLYuaB0zlB6E8Ucyk9MpL1hy2ACaqlJevrF8p9ThnNJiA0EoludoWzyS9wWlpqKpF5m7DVyTJyYgl025ugbnohr2EKvcuTsHoUQhaXh9tN3rmlI9my/V2uMnyE47d2JBxvYwOPtwMhUPB3zYPj8wb/pqJsRIW7dXsrWLWqUOiSAJIEi0a5QKUJlqp7T8T7fqNZ1wysI1EISGRZruMI/kfIFGrY05wOJdLjeK614AyTRNRZL26hjGpBnvRBKz2xdDKmcL5Zo2foLYIwGn9SDBmZf4A/5I0Ksd6Cl86cVwkJE2v7uerZsXVFwOhSBsIAGTImgITQNsnvRWpbyyvJaTFRjXsLQjOhFYKFSkeoPk4WOkgxnNUIyatvc9jq39M2RFkao2QibMQpS9uiQ9sGHF2vkYLaVVb7WVi1b0EMpEwNi05Yjq3C8cLQREAdZnYGFh9yFJ0t5g1MvheqfRki9cx0MhCONPxkRbQWSqsNrQKa2vr62UYdEBzBZF1hGKI1OzmsVNDyHCxJeNEoylaoZ0oM3ZR3zEd5ahmVC1YIJ1cBcwJQSjAZ7uqnYtsLIEHc0BLeaIRdzLjuB8vsZQPqC9LC58Ay0f2E/CAGGvb6N5HvZWMyQGBgB7D3tPdyjlYwPJqNdO2dQ6zXp94/r1zWpVNi0CCOIMzaqp6c3SpijGQEg24hdUSWqXe0dtaCEtzDB3Ry0HzXVJIRz1e8D0GDIGgsiqvte5DE2U8xOGsam/7Ij2bl9WMNO2rThh8Hf3GSOAmZNAkxbW1XHcfdzrv0MhiCZVijTB6BqYOzVr9e3l1YK96oURNKh99yTvVcShhLOsduEOH7F7XJLnZa/AO0awo+WFzV3+RCYd8pBdghitwuaFrImOpjf4bws+QwP2iMKigzMGBLtF4nhPxMcRRkeW5V0zOxwXovGYD4YIcrNe25/V9QzCUAjS2iBrpSrZakotYEirVqh0uk0eDnOQvbFhzyIOKaBcMPmpi9h1DkmOc5OlfP7Y1NRkhCe7mnGjsnozO9YNkDY/dJ0kCIbfUZSfW4QRBfDHE7MJH643G/VauzvbgLFWID2X9pOw07aY26pffBQ9FIJIG81KrWmVS/VWq9PpKIqqdzeJgnmHc6w75GLd88B4Df56+25oXyIzNTMR8eyMoPXK6o22AwgCwxia93RXknorx/BD+SYWZhL+TrvTqNc7XYLYS9aZRSCI3qqDmWIfBp5DIYhSlw1DNsuVZrutqPs25c6Zg0BlGZ1OQzbGunf1vE8GBlcsmESnUqlMOu63l/1h6t5uVKtj1fFC/wFTyxZPwM4pqjvqO68cI3kPnUUwfSUdoKvFwnZF2oGI93onp2aivF7ZWF/JtfrYZDYUgpg6VsMkS5JkVdMOuGQjeH/AM5Qc+60CWNW3bZ3Guqp/uuyMPxCKRCORSDgY9HWBA5NoaBsPGuKdHntYbyywo2xWaxYzrAz6SxfMFIOpmZjHqt65dbu4q8YIZCauzWZ4srP17LNrhX5wG8rnCjoiXSrAyhtoFA4KRfL+oKMIYvPDwX0I7U9OzM4kfX4B1o/Irt5caTXaQJD+viE0sWATodysVGkRTXKoUgE/HKHkjOAxq3f+ebOk7KjBA9P3z89mDF3efO6rnXY/qr+hEAQzMePu9JeEPQN3dzZAS+jjKZjhtdvtjgMsO1DVDbJ0QDtJ0QxN7Zrh+zOZyZnpmMcDTk5gaUSVZala2bJHCuNlCCxiyoo+VhmOQA67V0Ph0HQqbCrt7MZKtWGbDcFyYXpmMR0Ta83a1sbGkSg9/hwOQQ5kzobC9lb07pNAJuLlSLPTKZfL1T5UbgeSRXNru0RwVD0ztBjwe0Ft3y2fmEzGImERdgYAfqbZyecLpVJ2peqAVXQ0+CNLhebYybnZxQRTr9Y2ssV2V/kiRiJzM1NBD9bcWt9q9pnX0AnChCcDd3ePijsEkes2QXQHGBPd5cd4W+T9qgMT42AqFQ2HdizbhVjUxzB0t32BJeJ2dukOcKRSOzCt24/7gr6jee/UQy+ORFgZzDaypR2zOm9qen52mmGwxtYNBxLEtvWkWTY+Oxu6SxAeKpzF5PI2WFV2nPFROqj/AEsIXzgES4KxUHjH4ygfDOztATFUuZa9fbNUrMmyU4QGFxK7y/5jZScMSqPR6LXFKzxPYrpiUR5FNXEc98VnMrGAprXz60vZVp8iDqsHgXVWYIc/GklNTfrtJtDePMqA5x/KkLau31hvOoMffaI2lGgUyybnZpNRsHzZHWLRUOF3L7PTKG6vrbWaO2uudx+P81/c3gY8dsMIHBf9gZmZ2YlJAcairBhKtvRKuUySpD8Gu0Cwdr2+emel2u/K0bAIgoHlrkeECp9Mpb22GtUmCE7TuK43N5//brHplFZwnF/Y4bxJTkxde3HKB9todifp4GhxL4jRqRW219d0XR+vDmtPIFCOkzQz9i4EegohlnzgwRcJgr2SynjDkk6uaVVQdvijmTAQpLS9eueOeldpdED+nm73a6Cn4L0FAltKHnw9er3J+dl0OMKB4Q6129QYlgarTE3bMsu9DiMAXa4vPpXyHFSE7zptgIDwIWCGpgI/Dsca4y+S9YLOZYwC2FmDcUZienZxYR7wAUNFUjB0CizEcFhinUrHRcaobdy6s13pW8ohEAR8G3NcbHIiJAiBaMTH6GBugoNZm13FcOGUN5rAlLY7xjqx0nZA2n0FgN1dpiEFq5NMlKTWxZxynJgHoodQkRV7d9dYL08gMH/ftXTA3iKggdUGG6LEqD82wXLc7EyAxtXi0re3LnBgztHCDIUgjOCdfOihOM+zDA3TJrlexyMwJuwqe3EMCpCQa4QJJXKvowjYlNh71h2Y7jIEmkU9kSiUVAcRRCQrvrETJJyaf+Bhj8cewetym+EYMarFMkVw+wieLgxTKSz9U3sAH2LoCWIv+cdii/fdH8bB208blpSkWp01gCtdLOEUnVBGVZoSWKHsfQnju9n5/GyFwtjN3WH5rVXJwnElpO2V1W499mZuNE3DeTo+2NjQGveQpltVttMu2NjFkWP2CwNHakSnZuenJyx7lwxsrpCCQYYTCU8gzsG2Qo5RVbCpJCna3kXV30eGmiBw8IsoTszPTU/6zVq9IbXg6shK0BI43jaWAIt3MUFxhqw2mn0L3V9RT4oFn6ANHOkJhg4O/U8KOvRnumxln1eT4QgGu9F3j8ix5yCsPxAIwnKXgy5DqlRCDphG8j7flfvuSwfAb46xvZ3ttOXMRAYcx9ECAW0KTHtJGpv4F9z6+nrfzTFigoB2Fxz4zzz0SEIQauW1HBwoWq3JOJ4W4yGSgS4EByekZMAvN1qkJsPO6/HWus0PmyGkJxDaVa2OTyBoNLbVQiKdMsGfyYHGQ0yl0gYPBBkzWAeQ0YEgkgMWej3hxPyLHuE94GdPy37/e7CvomawQZJnSA5W4WBIT1DEJDf1baJ80AvQgYKcf4uYIDQLDspTCwuLPsOQ8rfXy6VqrWZ5vbpu7yA1VOiXCYIX2VJdFVgWCrQzVR/fYRc7CMHZVxE/OJ8fK2HBgKSmlCu1ml4qtQ8QxFttqHz0/KocYQhDrsNWAWjuwOKdHON5cJ7o1GQmrSotudO++f3nNE0nGFbBeYKCldadsQHpp3zbvv4dtSIlCKzZRKNp2P2WZDRJ2lq7vQVHIamkODU1vxgHNZYCpz7CyBCclyc0bzaXL5Vrhm1XZMnKuHYu7dhikV6zFQ2IqjrmVtFQrSrWNpst9YA9oorLRnh6hJ//+Vl15yAWqCVJmuO18bkAF+MzYbpdLpUq1cr6ehHmblu02jRgpyP4N969lHqlUm/1bRqLkiAAmJicnZufA/s6uVneXl/Z7sCWJCIw+8iVdNyLE+3SWkMQfSYjxMVUPp9dXaPBMB4IQpjjIQj0X91xC6iIOtGgKIHv6LFeJqxztIuwVHRQxddRquasc3RXNkA7k3S7A4FvEfxMjAs0IT4bodr5O3e2trZbUgtGKbQGg6yAnzxAkEaxUpf6lhElQcBWIj59dW52BvqPcjZ3Z2O7aJi0KEzO3zfrFXFZLq0uV+BgsY4WJsFLQiDEsx7oFUEDkTf6tQQYqGps52zdFUzw2xAMR8J43+utA4mxHxk2DB4/1kA1VCf4UdyXElo0cI1qm7vjBBzzo43P2oTiRLPevr10a3Nja0e+qqnEGwrsmFHBrVjX6nlzazN3oTOlDhYU6RmF9vhq/upC0oc3SqXc9tbGZsti6UAqQS4PYAAAQABJREFUNXMlLhBKLZ8DW6Ka4PHF4xEwysLYGPQtBmz/1zvPq6XDYo3ml1LdFP3gFt++6EC6Auqj0WR8oVxIwR8Z+3rcYYmhqe72vTBmgDnl4Xej/NWpbCuqsrW1Vbm70gHWLx7R3n1ZyWZVxbYvKRZLq7X+hULXg+C4NzW3sLjoZ7Dmxsr65maxJoFpTHJxcSYVF8A97+2bd7azTRYWciIxOBoh4AsEpoDqpqw01Gz/JRggplrZEEx+lyD+VF0a78GJp5QENAhR+8gpR102Q+zL1hXtr2yOWkS5um3vnKhVu1ToygMulwU/nM1aXnoeBl3wqNEAHyf9C4aMICRNhycX56fTRKeeXb2xnst1TN4TCk0v3p8WaKXR2Lz1vdvlcpui+GIwkq42U6DNAlt9zGg0SWE8+5u1Rj7k3dUPkUIo0nV21z+Wg8QE8zVwhnBckYtTpC82AYd0DZI48rj2HESxvQ3AKSviQS+4yHM6O8F2aT1nH9d4d4YGxooeMAEUSFyv3HkWjtGB6PB2kK1HyAjiCfjn5hdSPqwOZ90vr1UNbxBssSKR5ESSbRbrpeL26gbozuErUJqwL7K0nU4n/X7Y16xtb2/eGo9XQ1NpOeMcT1heFTxap31c3U37fMkr83MZOP7ZQZfewEPlukTRXHiqRXbujm9GLmEzq9fq+5u3oTsTI5mISBuqUS1s7hBHA5XHAFoEZAThI2kgiJ/FGus31jY2ddYbhKN+4rGgzycXYaK0lS+Xm+CLBcZUsLWhzIoT+XQ8EcYw5dbN5a3+rS0HqRQDPI7ue4geJKXB4sJQnvMHlSqcKXp0eZoOJGau3T8VcNRCOqY1lUCpJnkoLjzZao9vYNoyat3J+C78sCrjjQJBKNgzX81v6l2lJEzVd4eDfVUSMoJ4YjOzUxkW/GHVC2XJoAOhxMREIhbjTatWXFne2CjDobIgIrjl6+pS2Rb4d2rC4FC58fytMU2ODalalVRwommbCIKTY3AdMvqlQpjm0jQTjscbWBs8uB8YZdneln3x6StXrqRJcC0L0N0d+fdV1QgjgU+xSrXWJFjbwfsYj+mS746tdspG81w4MRkRcLlayRXQ6H2QEURMLk7C6YMY7onNBVoSE/AHw6GAj1FrNXBevV6uHzlSFlzMao2cFw5T2sq2lKMNJ8LKPCMppUJ4UvUAWAJiGBM0tGywAH68zogwjFcMxwVDIVBbbBN17cC5YWBkx3o8yYXF2ZRob0uCwSmsqg7SGCKVHg74KeGslLt5I3txf55IJdlPzPbSMDcbYpTt28vXEfVryAgiJBdTQdt7rCeOpQyDA0UuGFQyTL20tnz9Rq7T0Q6v1RgtrZ7lYO5ptlrtAwcu7hd3+HdKuc3P1WKYDQId4q2VoKczclFo0Z+ZmkrE4zdrq60Dnh5hlRp0HFML90/5BVtTBEOFLkOGD0tPOehSucT52tlbN+pjm4IcFVSIT8/NzZGUnP3uP2bRdCDdb+NoPn395kLpKHS2OMGHWYsk4fgmwoTDLpTi+q1by6vlYy2f1Rl/w6M3m75yo02C5y7YpeLVwl5O73drZl+g2ZG4YGJmcSERjZorwTZN7XVgcNhzIBGfnZ9PwVomcENV7XNzHOONyujU60FVLm+sHW74+sZh4Ig44UvMTWWS7XZre+k7e5qtAdNF1oOYmqJSYJ4Do1LCIglTltSO3Jak4sbGVkk+xo8BxUYW3ZRbLdqeAcPo/8DwH1n65yfknbg6PZkIegg+PudV5D2FC+xnDUajyYwX/N/CmF+q1bZvbZZbTthEs18omFGOfta2n/3BO4qiQ6npmIBJxeJ2ZV+1dTBMH/cICSKrrL3FBw4GASOdtlSHq1KCdZxyo6WM59vrAQ9DabU83VYbTjgbC0V8kw+mY1E4RwAmbyEwd74rtCccCoXDfjgeHTaeteulre2N5c0y7ABy0AXL6TArckblkiwXTM6EPZaUX81WO6j6NWQEUVtlxsRoGCyD6aFhNItgq1vO53LVzrh9yZ75QcFIoebdMVEcU0V74vMJvw/qgY/NtQ74ZADDnZA/wML3Z5jtSn77zurGdu6Ch3yfWXQEL7vm7vgJ65sI0r5oEpzfn0hlWFqpbi9v15CNlJERpHKTTETCHobFwEN/s1mrVhtNOH6t1oEDyC9a1hGG16qbXtEZmy24sCUfWCkENYfAEZahq+12aXVts1CATTQjROb8rGDZgfPF4rDYen7Y4YeIzM/dn/aoVWXl1vPZBrL8kBGkerMK64IwIrA0tZLP1aV2B+zIwMfxwcMokYmNLCEVCBJ3RAVjfEQAy+a9ksEmdAZ2I+lgGV3ZvH5joyXBWSt7b51wA7a8rD8WI/o4lmYI4kev/ctU0tOullZuPt81wkKTBzKC1KXNaDIBgwJLUfJr6004NnaQFX40pTs/FdA1B6rKzlrh+aGHEQIcW3RYziIIlg3upQ9EAarARgCt06lls3ee/97GSZZae+FHfwNaaJqCY4v9gfYApoAI5fZPvgjcdOnVDfCjiDBZZASBJa5WSa964ERvvVaWFPtsEIRyDispQ5HA3FOCyh5WDuel21j9TqHb9R6UAE5YgYOeNVmBgzhKxWJuEzxcnJfQaN/TsGvBS0ngScQJxjpQdqm4miC9nep29fDy+oCwHKyVgZICdYZkNMGXBCj+lA44kHWKduPsUoG9IsyVWp7xedOpr1rlyXbSOmRtZapgeGDv/AblVQGsOhotw0E+G2xI4eTEuI/UFWlcZhBH61UqrJJeDAzgq0gX2NARxMIcsPR3FLVzf8OmwnqlGBA12BUiyePwe9vcaDTaqkF5YW+FvbPCbllMpdmC2VurXM7eWcs1Gsh0Mufi0XMAUgAXVBgc9N3eP4Oy58jDCCgV7nD+aKXg1B5kGGUeQZqws6F807jNczSGV7+7BV/qCDI9lIXewbJms1qtRIIBliBAZ9XpyGDI2VbhKMBWtVhsOWQMc0hqzGhXyzwHbYpThtLNLbO59fzybaf2IIfhuzS/TM0smQXB3pmOy4Vie/S+obW2prfy5WplaoohKFytw4iqVioUGyocEKyCPxv4Bh0Ipy5Vy4IowUjQIXPNhlnf8oqwvHDExHdA7JANsQaUY1zRQaWgDODaGIHYsEGgVSYqsGJEeC2GbhSz+UIhv52rKY52ga+3Sl6YEsNI0CHaA0nKI6iNY0m80AlyDJCxPDAxqWSptTWBpNrVar1Rr1dbXbchY5Gmp0w7m2yO51pLRTDT7inCJQ10cIPOBYswlL36iFTDTpbtgPv2u4jjGOeBM++9cByhvdtbAUfMFx27jBo4LhiEgakKLkiNcy2MEcl2AnB3Aez/3/NkcwnSO7bnYdlrSk5mr5NlGwtBxujUqNfvyQ3nIjA+BFyCjA97N+dLgIBLkEtQSa6I40PAJcj4sHdzvgQIuAS5BJXkijg+BFyCjA97N+dLgIBLkEtQSa6I40NggHWQ8Qnt5uwiMCoE3B5kVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUTAJcilrDZX6FEh4BJkVEi7+VxKBFyCXMpqc4UeFQIuQUaFtJvPpUSgb4KsEW85UuCvEU8eebL783jQk8Mhe3o8Q1e2nsB1gTsGU98EOZbSYA/e8+pJT/jFT1YGS2U4saeJ7pUaTuqDpepk3KBk//3Hk1z68f82WBmHEvuz73yFn3jT+UlT5wcZSYiPvuS1MemZ9//hM+mRZHehTPDAuyyIIF4o0ogCOxk3DPvfPzzx+kjx208/PiI0LpDNB54TMzd6CO8UgjQZW9j3fvBDn+hB6FEHCbxv1Dn2nJ+jcfvDD7/59+0PzOi5OKML+NHM3Nde1UN2Aw6xln7lpTFu+m1buzk98+qA7/Fvd38Yn3q5X3jxJ+2mt5eryw/s32BLvQTuLQwy2XrL7kKhkMk2BNwwVMKp753q8gMjL4TNmYFRyYa9cu7MfPZeDkiQz/3B5Bveef8fvSzbTfCZx/h3/MjfPvpN+KG/7h31n32b9cTP7eXUy81/wR7qJVhvYdDJpvzJh37vabO3XHsLhU62bn5IccNQCffl4k/iX/y/fu+Z3iDpLRQq2XrLDUINOMR60y/RkMhXHv/AJ+0cv/SJt2PYF17/lpsY9oG/eefv4pj185/5qR+13+xf78f37x975f499mGp/k/fePg9B54MeItOthxM5qyZz7xiQIEOREcnG3rcMFTC/SPOPPI9+Ahe8ReRA0Uf7BaVbL1LYfV5reJv3o/54BzcP40vdJ88RnzdMsMpw/5RI37asg4FxXdUQt2/v9ENv/snAY9+pHDwSd/3hzIcXLYnv1rofP/thPBc3wIdiIhYNstCiNvhmhoYuLfj1EPfkr73OP6qA+Xv+xY5cPDBvvF8aQbsQbCn/vjZKszB2C4jH+3+fezr33n0VmXhN+0fFn+9+2z/z6kjlSxW/NZ7Hv7iw/tBB71DJZs9Rb/vU8JH3v/ZQSXaj49KNgxDjxuqSjUx+gsT2P2fW/za3//L/ZIPeIcOuN4EGZAg7/pY6vE0j31mvZtbvPs3YdWxMrb05I4AUm9y2KGir39k4U3P9R7+nJBIZcOwX/jI18/J8AKvkcqGGDcMlXAB7JEJwIT/15/+B2QEQSVbz3U1GEGKH3/wWx7I60938st3/8nhfsyP/fhfnCzDqXMQO/jkfc9WQifHu/BT1LJFsQtw/RxpUcuGEjcMmXCLWKCLQxDrnINHz6+RydZzjoMRZMV8jc2PzZWd/L7R/eer2CPY1cAzxsnKvScPTNLxg5P0btxtdCpB1LL9HTa7U0oEf1HLhiHEDUMm3P+EP9/F6nvYDALMukkgk61ngYieQ54UcBr7BswpWj+v77xcsnVZf/X1K49i5BPbT8j2w9z1nVd7f01j//r1vadLDfvW+rXCD/r3ng14g0y2G21bktV34G8cUKL96MhkGwJuGDLhJn90/aNQ5r/5UhDZSjoy2fbr4py7wXqQ+M/8+cOvrX+Zf/jZbjaP//JfP7T0l/yn4cf7nvv9L/xwurD0zQ9eO0eC7uv/+qs/NBPOf20l9Qe9hO4pDDLZ/vwjr5jy3v6i8rp395RvL4GQyTYE3DBkwmGf/Od3f/GRlb+i/sjbCyi9hEEn2199Hsth33ozFvntc/I9X9F1cohV4i3wovPeK/zkE5XHSLh/mnjymdf4YSV9J8JTrw6zmUd/axPUvN2gJyez+/R7TzwSpQMve7J6ZqheX6KV7WtvuBZkYq99qtfczw6HVja0uO3WFKpKtazSO6fZ6E/+49mI9PgWLXDW+3eXG2bPyR7v1RbkHJ65r10E7kkEBpuD3JOQuIVyEdhHwCXIPhbunYvAMQRcghyDxH3gIrCPgEuQfSzcOxeBYwi4BDkGifvARWAfAZcg+1i4dy4CxxAYYKHwgM3IsWT7foBI6+xk2TAnC+dk2YYC3HkfnNuD9M1lN+ILAQGXIC+EWnbL2DcCLkH6hs6N+EJAYIA5yAsBHreMZyOAU5QnHBIxXM7n62cHvaRvXYJc0opzhtgEw0UXFxI4Vv9n1SWIM+rElcJBCBCMJ3L1B+dxPKesrzpILnSiuD0IOixfaCkxfl8gGMpcTfoURdVh59y9eLkEuRdrdTRl4hKT6Vg8kY4ynXajY4wm01Hn4hJk1IjfO/lx8cWFTCbC8bopNTq7267vneLtlGSIBCEoimFZzuMhNLh0aGF0qa2ct3J5rwF8T5YHvP95AoHM1atTkbCAqY3N27e20fl8cRRkQyQIyfJiwB+KRilJarfbGoa1C3nNdBniqA+gL2FAuxucm5uZnIwKAqmo5bXv3XAJcmEkSU4MJ+KZ6WmmWqvV6+DkpG41m4TLkAsj6bgIOMUGr7x0IRIRcUIzpPLa959vI/N95azSDqsHYXg+FI9Fw5FYIkGLgVarpWJYRW/JinyPDlb7qFcCZzkWvAKQNEXgsKTAWapqGPstiGUaOiiINM1RGiKCJMVQaHZuMu7zYI1Wo17f2Cg1NWdM0v3+AMMy5VLZNNCANiyCsOFIZnYmKnpFn5fkvIqqAICFVrnZ0F2C7HIJ/GqIwSB0qazgoUjCFwoZzaZ8gA6GprdrtWbbRFPXfVD4pCgEy0amJmdn436OlItbhWott1FXHSJjeHbG7/N///ttTUUD2tAIEppcfOCBKM2QJIHZnlVscbOlrZLR6TqUOwn5F9ozHJriWIowTdEfYCkqmslopVJLlvfaYl1W6lmGsBSYvznnIhghPH3/XCrhIwi1uLxWrZZydc0ZU0s8fOUl8XjMK63gBhrQhkUQQ260LU6gqYMOSP0T1/A1mlRVtxOxv3eP3z8xNUWaFu8VaZIKxqI6I7QVdY8ghqo1E6VqtdZUZBibwvDLCZ8hQTFiOJ0McvDtGHKzUiyV685QTuI44QmnoxG4onUTTUM8LIKodSLaUI745+XSpujBzEbTJYhNEDGVmZ2doyyLZlmSwD0iQ/oYTdf3WABuWhVJatbqtWq1Uq02nNGy4CTN+6MRj/3pWJos1cuVDprW2gZlkAsnSN4X9vGUEEpgMhrbsKERpCbH6uqBCaddcDYVCGNSy5LvUY3HBSvXm7q6sHCFsTCcwGEnH0ESNOO1LGyPIHAHc025US9vwWXpmCMaFpsggViEsHdKWLoiNcqVfb3CBSFAGxwIwvkiHp7yBONyDU3awyKIoZq1/BoVCuI4DsoYjLTxJHlaicZLqGRHg8CxVEBU6Ks5luV5OBcIFEtKs9k6FgrBAz4ymQr77DPsMMwEkORDfQcG83ZYkONIDycKnNdLKh1DQZDrYEngFBlIT8wnRDg9VGk0Nm5vFmqOWUSHma6pgzoNpzmBRfRlI0rmGOqWYTW2l3Cdownc0lSMoYEgOEFx3nCkunM067E4zniAUyArzgSDwUgkCF8uqDHX1odCEC6YCgu7m8AtXVc6h6bnOMuChgPU5SSLk0zAq9Rq6vhXq0mwb5+7/0qKg8rqbN65fet2tgkKfGdcloEpnRbFYwTF2t8bimtoBNHxZvYWyUUFDDc1GcO7GZEE64tE8zuNJgrxh5AGND8sSXLJVGZqOgXT0HwhSzQ2h5ARZhPEs0sQaPnaB+ZmSqtFCCJH0zzO0Czj8Zv+6nZWGlZt9V46kvVE5x6eCfAQpbP57PWNjaqxp1ToPZnhhARlqdppcTpG0Cx9UDs0QHbDg9zqFGk+MiGCmhI0bjDShh4Ewy25UZMcMZQ+DhpB0wzD8n6fSFJcLBZPp2JAEIom1u3PAf0l5W9XBNHq6qZ0VZMajT1gtHYb93hYmg5OTMDcBGrJG4xGpAp6IS6YIu2LJP//9t4ESJYtPQvLfavK2vet9773vTcz7w1GIwnHmBEwMECgkRBhEyLCGBkQE0YKkMCWwxjLA0GAEIuw5EBCIZAYrDCLJBjGRtbYjIQlxhJiNDPv3Xt7r67u2vesyso9039WL7e7b3f1vV1Zlfnuy3wvbldVZp788zv55fnPv51MOoKJg/H45Ou7x52R+2rflVuwJp0TNBAnmCDteYIgSs8IloYRlLQMHbkwUOqD03LHGQPcFVyc+YhxPB8JR5PJCAmWTJ4Ph0EXNANhJbiYIa+3Y4WDQfCVg4/I9gmKz98chqaiJEWSRFZmmal6SgQT6YH7uikVyeXiPC5J3ZPTk0qlPfaG/eryARg3Dug4QrI861CfLW4EQZTuMLA5SJKMZeowCT27B21wemyHZXlxw7koBMZkC/kkCcMGjhM4ASalgKHbM9IFbL3dDh/kZVG0CaIbsnQlpwLwApsMQa5x+QRqvwxxIEjdoU6f417oaD6f4Am5e/T1r58MBdk7+tXZTY2bTKyEECzYeue4yyunOtTMlRYvP5qmNmydsDBBt0iMuJiMarLkUBDA5YXm/0CCvk/gXCppb+l0FOxHiiLpMHFWFXEinI7mv8QtLch9meVYdTKBlweYc8Fc9tzAOz2c5DgTrL9gB0QsQx6Pnk/ib2ltCT+x4Nnc2iqEMbF1tHdwXFfs+CFvbfKAgfcvE0nHgqR58VaeR8QFEgTEktuHmIqGMY64VAnBcnnOlXnEdvhcOltIsEwQzFbBYJAPEIipQwSyNBEhDFlRJuWWw9c7a06XDVUidU0FXlimrYje2IhgLBXjwRAIu5VBtdJ1eegNb26uFArxoN6vPN0/HSjPzdI3BHfvqzrugdbHxfFkmNG1FxB9dcEWTJDWAYKxZJClp24lEA/cIrZTzGMbk31zFeYcsRiP25oVZhjqoFYfDnu1ehPGkQU5NmHQkFAMqAFo2BwBH+H1jeCT6WiQBcDAPNOvnfRcnhCHNr95LR6nDKN/8vSgP5RfEPi6+G58U8coxI2zBJ8KMQqo9nNviyWI0ifB0zbK5nDinBY4n841VZffhFdRw1CSY/Ob26tcgAsGSGmkGAZwQgJqCMKw2ezciAa4eu6cn+H5ultFQSmOS5bAJReAGYjSHxwfnkJM+ZxXnOd0nKKS+bUVPqgPh61OdzDxnqYMt2fqim6iBEaGognBcuCFsliCaIKpQRrhGzjGMGeOGyqxLqFjZ8Jk5unui3MhojaUyay+sZklcNwU1XarL8sqzJi7XfggC+KF+e3ihCX9RVEunyuUSsXc1ONQ2d3drQ5dTaWBGiaZZDxME7LQaA4Vj4S3394dKIZwsRyuOTB3XDBBhuKwFewQMR4lzywwZGJdE2q335Ybv0JgEV94tLWxkdANTZn0Dw+qo9HEMLTRWDQhWNBOpXdhg1iXQO7NzZWVBDslyPF/+HqrNXD1oaRC6UwqHkYxXYAqiqrpQf3qsqNQBONiWVW4/OHhHxZLEAjboMB+hYJV6HziAdEmBO6hSQgZChXXt1YjiCAIY2nSPy7Xx6IEzIBx5OGozncmBm4YhilsPlrPZnnIuZhIx+Wj45GrChaKQhggBMrCa86E2LQJxN6fz5hwCImZGl4MaeIaZDcBRxGCCdjx+HNvTrRxtxBUJMLzgdVcLMicOzYNadAX3VSlbwhLJwtrW+tJvTWq1bqKLPYgt0FVLTApuTN02OKRgUA4Fi0+3syFaTBgSY3awVFbdNWkik0DySOsrScDNqosXUZWktF4iLIDO+RG3TMEsa1BzlhLF02QbCIULgBBIK9wupnSsPfcYXz2m5v/0on1R5vrbK15vLNbs8sTwfzD1h5eNCktT0qw7kKYy8r6egxy1S1Lrj/bKXfGLjIWHjecZPhYhLVfcxA5pCqycuFkIKOlFMOAK3WMCf3lgXTflWx3ghOayqIIAloVEwgk8hCXwKdSz13RhtjreCH8E0VImqEoKpYJoMpQqBwd7O83oWDCfbgvfD+4z2PFYj6bzeZi/NT2qwmtOoy6Fw/kwiW49QIoSgWiIQZCsxFDGY8kO+0RlCt4fMLFtRzHAUFEgo6Annpmtr61keX+6Ag/kEURBMBLlkqZZJJnmFAM8irON0NsNwX3VSx7EpxIRiMR2lSrk7ZeOak1x4sz6F7c/Uv8JVg298Zb+Ug4HD4PLDF1mA8Zrs+JIVmPB0UZtXSxN5jYCigVS3BAkK3NM4Ko8c3qzs6xnZDxGm2LI0gg+6GPFsJhCOGAqDs7kne66eNOU/CAqorhgcxaMZfTjo46LUqu1XuS5AnHMMGF8298U44iKeKCINPoHJcJYlk4GwaCoBZqTPr9M4LES5AwE97ayAXsEcTcUFq82PJceNZ8bF0YQaBqXH6jGODI655zKDnGTBzKZZnjzskAl19/VEgkBqhQRyy50x07Erkzh0jnp5J8ElJRsjDJvGiLDKUFmhuqqgr56i7VbYCiXXwkFmYhdcFUJmNRNe2SLLFsmiDDxXySoUmYuOF4pFq1c+c9AuUFgHP9XRhBaCiJFWSpmzMlSHAYYJOFJOi9Cg5sKrP26FEMlXrtZgPiOESYc94M9HiV9pw7lgpnUiHiYsCFkRcLFNF0p9sbCcJIEGV3SgCRwTCUCgkzGEzQJZDBsHCGicTi6RAfikVB89J0zaJpMvOmdVS2KzE7B4jLLS2SIHyAfcHnAQQZTdou3zSCMKmN7ceP6G6322k1wSGo6+7OgZ8DQkWyaZ68HD5gJOFKiY1er9tqt1otDNFdmcCRwThU04kQqGFbeG2CYPaQEs+kkmGKwsEsLsumhRPpNzly0pecCIJ6DomrnxZFENNQJ6M+x9mRdlc3nIslF5R/dPUy930GgmyV0nq73+oKI69pBKY6gaRfGHsBOvifosLxaLKfaMM7vN3tDVVFm5bhu+8enduPIhQXCvEB2jLlkdAdwnhLUNFEHnILE0lO1ycSkEbBkniATxunwRfeis5JsvyWFkUQdWhyEXaSSQeu+2vAt+5YPv0caLHJjdU4JQ0b9QG8+byhXJ3djjps8nxIZ1mouXf5bsEYBGciGUHo12pNqNMGA94cN/+qp4JLgWI4lkRNXR912tWOoKBMOFtcXS0kOWsijoV2RzBMbpsNeCxO4lXv9JbjF0UQbTghWFxDQ1Bi4rKf4fowSacvkqduEWdZPzGp9VWa0ob1OoTdeYkfiDpswNsaCdmzj0s0MIbkIjro9sLR0VHZlJClRmXBfJyyAzeg+oYqtE9r7SHkAUdz6+urxYhlSf1uo1zu4Ficy0ESJHnhEr4U/v39YVEEgZqAnQBm4fTELs5rb2C+IkFjAEfY8zeja9jhVIADJcaOeWKVK7UMXRPo8sLaCF4h6DASCUC5RXh3kwRBQl1S+6UC/v0JCEyi2GCwTDMH+LTCyVyKx3Vx1Dw5Ou7KOB8vrqytQPlqcdKv1k6PK0NI5zcgxlOVYbnCizCty5t6H39YFEEAErkDQQnjKEHYBEHRQDoTvkibch0wXRoKwI3MGyRl4GPvBNnBKlwTBFE70VCIBaMQVFexa0dMi+zYmKFkxICyK4nDw2UShAyE8psfWkmQcq9R3turD9BwfnV1bTUdxORht3FwWBPGZKq0kmdVsdfujyavk6twgQRRuqPxuBs+q1+NovHHXMADY8cZN/XJUEAoNkszBmRoqh7wXF68M3RRETs05G5BaUcIhQmlUhlzGvI+PYCIcrFQMmF1a0vUC8lgLLfx4RRLDXuV/Z2dvomEC482C/kwDtV/muWdp3UciyW3t2OsOu4DQaSlKoAXwC3o7wIJouuipo3PC7yjWIqJwHqFUCQQ+p1waWIM2jR4uuDZUrplPKEwTAYZTxBYRMwzAUQwu9CkIUIyjE0Pig6lOxB4oEIFICiPBeFPHBckg0z7tA5uwwU9Ei80C/OfRDoHTnO5XT6s1LRIpLS2uZZMEONxp3JSrtSGiXhxda2AaaNGpdzyQCQR3AKkFjqy7tACCQJSamNkBFU5bA0LlcJBJAX0CIbBP6K5MwqTDGvICsQ1jY7Idi6XCIeiawRPydqVNQdeeD7c+MHUEF2BmQchCM1Gp52NRkNQH2sqCcZE1MLaqNtdWlomFNEJ0LalRWruHXXVYGljY6OUCBLSyfFJrdYe4OmtrfVSlhmNOru7B7Vlan93940mDu1lMefeFkyQkQxFa2whUVQMcgQWYmjeJgjqTj0lIhDSUAjZtcZlobbWW10JR4k4L7WGiDvy3Nl9pmbYJd/BjADFFTutUrFgWcQZQVBIWip0RALGmSVtEKYI9THhYlJr/0jWAqV3tjPZEElIp7+112yKFJl6/LFHgQDSbpWfvXcgwSTKA5s6eT8QxFQu0+ZRYiyDA9aOxYLIHe3MsrVEIKdrAIaiUamDi6omtwcjVUHDpQAdNQ7DLFQP9tQG9Ugv5RkoY2E8mWTAAGhrWRhFWfFMd2iXj17OhlMcmM4g/VjoNnsYxmc21qNRQh1BDs1Of4BmkmubjzYVcOCUdw+Pa8uR6d6r6NLIkarzix1Bnt8GitHJ9c0UaxeAggnIEqeYZzJANZ9UIR8LhfqV485wCM+f2EIDRRXBkKkddTrMPRfXS590AVOFWn5bNTl7PQbYpuX9z1M0lyApRtiLjGojsSMaOM3YGgCJKa32/mF9oFP86vZ2Kah12rVd+MEb+hWAostjxYmIsKURBGXgRcPAa++MH8tmCNT7Tr/5djIYqAdQzATLlTlGZb6vgRYDJatJz5jXbnneNUHu19isynAmcU4QgnUm4fqWq93yE6QTwkp6qgALKBo4C6uyBqCWndzc3zmqDyDlbPUjj1NAkP393f2KR/QrmyCK6EgRmCURBGfoeCqbBvOROhlDOtrSXUlMJLrxxtsRHFdDLAV0MBFNIWRI0zNA3fdaMNb1h9SUJJjCCclCmgqe74EU8SUOeeAfh5kkuI5GiomRLAcGNgKFxJ6eRkcDwcLqahYTRoe7O0dHzeuSu/kN1tx4H1ixLhGik6mtlK0168NapTWUHJH9svWX+BB7tL25EbV6w8ODckOwFXw6ni7FKH0yaXUHY7er3t57BxAuOxry5zqDvQqGKzUczsb9acY+xgQzoXUTgt6L9KDTaZ5Wa25XRr0XxIccsKQRhE5ubKXtCk/6sH7SEuSlu5Jijz+ej0Y63TIUXe5PlVN7se8opYv9dm/gzuP2Cv1lgU4tDOMXBNEkcdmUtllhR7vYm/0Jp3kE1qgj7W24914ZyiaJlxaZV7gzrx/qKEGgEgJk10LW2w0VCkXZ1Pp60lagdaF+0nFhUQk2sZJmmYEhyiZU1bGLlqRXtkpRXOnXqu3hZKnRsXc9Eyj4MQmYo0FVrrMH8eqBFsQqTn+2H1NVFBxRsK9e4O7PoK1AnWqwDIBzX6cpe8ZuuyyjwWQCYrW1wbB58PU9qBT9YvXtu9tcwp4biRYPvKKTBAHNOJZJGSNY8/LaQs8oAau/FItRmyDGuA1l+R4o7BynTdpHepIMFojUZgcuD09buFRMh81RFYKLRE+kS2EEHgyFURVmadfgm941ijN86LwSGqhbwjKLiymDRko0qTAClRXpSCTMURBeR0Ysgw/SpqV2K4fVAWSim7fQeo4em/NUL5b9Aa9WYvOx2mq2kMlVC5u98nyyWOBtR5c+ajdcIUjrEKOigXx8XbaVE1WaMAlYINAcVHf2GmN9+XbnF/sfVp4M53LoZNJHX7Dgo5APEuSnZalg9IOVoftQw3xZmzJsgoGXCzHtVBxKAUYCFARCU2HGokgUFm/oHO1WBxKMyV4A8TkmUHjq+ZeHf3JyBAHdJbf1tnIaJLWR7esAwKYrKlOBYCabTRGEqRvjQbfjSAjAK96y1DokOYjXCE1DmhBVFFGWNYRB9fjweCgtM/3oDsGhticfyq6uGp2uMnVbPz8O2EGQoXA0crZqEiRrilB9z4k4iufXmPUJ6vwMJYMguFRuZRwKpSMMKFl4MAjKnq6qo9bxUcvVsqi3yg7rA2tOuIqcJAifzz/ayGsUDLwyJL2BGoPZEakkn0qur4ZtX9OgX24MnQkiuxWVu38UoTQDYkZZBtZ0gnvWLALWBBzWa+XdpusLN9lSQ5pMYmUln811hUFPfO5Gh10wM2GjkdI61OCzCwFZqgKH2MvELGuDN509L8LQ0KYls2w8C6k002ubljwYVKv1tvf4gbDRDKZ4rLo7X3xzaz1vBGMQJ0Qoiozo4FYKcGxydaVUCsEKIVrv5LA+dMY+/YpPh9gQNNNIQHZFFAFVz0Qg+2dS29s9aTZH7gROXr8BjKSS2+8U4on9/WH3BkEIis8U19fzcbu+GAILSgBBusssHDLlB0AWghr4BFSFhrQF2w1jmYYybJzW6m0v5Quc4woZyqoT0WpOjiA0FOSA+vhsxByNApIkjsdkNBoG/WpzMxsKwlJKUqe83xDUpdt4ATNF6Vu4mQiH4yMJCAL9K/X7lSdPahMRSlWfg+riH1glMbf5kXyIFygJJLqUBEYWeyWd9fWNUioMzySYOSb9VqfvwLvx8hr3fTANTR4P7PD7bP7sWFhREZyrhqYNapVjSMG9r4Ul7rfJrEPwOBWM9exJ77ybkwSZtA4JSGVFuIwaHEwm4mhMxmM8y0bSKZ7BTFXrV/f3GrDsxrxCP+x8qYm0ODtSYnrPqDIRO9XOxA5+f1h7jp4VzBceFWMBWEiAYll0On+D9lGIbY/EEolUJp1JTqMTYbHC5tHeglYVveOOIMmxtsPko7EAe2YmQNQRVOeFAkASGMmrNW+E754Jb2hQdU0Avf6Oe3nln50kiNSmqEAkHmAzgYI4ATlHVCIepGjIk6IJHOoA9ap7u7Bm07m76ZVlnfOESWsMC4+T1PkUGFYml8bgIfSGcTJYeGu7AEXwIaGMY00dXoNwt2CqZFPFYqmUhiJ83JQgpiG39t49XWpMoD7RaqzeKSoJ5HwtVm3Q7IvieDTqN5pdSDnzzmZqFjx4HOpNgnQMLp4iMDoQxjV7BBlR8VhgmpMOb2lN6FUr5Yp7DyRoWd7pyJuSMPFSIQklYBAoPToR7IUSYVyD6XmitLW5sQ4WwDOLjKlI3er+Tle8ef4iv0MBjhYqChMooq3T0+F2cHraBtf5cNjvLleUe2/TABv4ZIzTCIQgO2LndXIE0SZIr9s1u1QkkcAoBCM5PEifSQnzueHeDqQPeKKC+r0wL/8AddSOJ+xRI7xtbAjgJ7dXYgDXYbRUzKeCU9+17QCBtUWPn5x0l+gEOYNCFVBz0ozwQXJKkEm3O5IkaQJvwasOr+XD9uIVzwwKFkKH082z2OcXj3mVX5wkCNQb6HZ7kqFnLPC2gq6gg23m3OBh6MP9X3vS63tD438VhJZzrDbqQMVOuFZoK94fDiXdDueEnFs+nYpydoUiWwzTkKrvPStXutrybLxnt68KyqjJwTwdVryCX3QJFjoFOz64CT1GELDl25uFMOFUeKqUnt3Ag/91lCA60u80CFEUAmnQCOyln6eL1wGkhiQ197/y3oPFfO1PVEed3kjRIF+PX5kAQTQ7OgyKYnHhMOhdgCD0OjiWusfvfb3tgtUICnC8T7rAXj3PDlqj+MQ0dGNesZ0kCMgyrqJQI0TjSDBJwgKeXCAApjZQDYTT02flpc4s5wVmyeeDitWopdRAEDoEZyzmYg5CnS/uaKmg8wuj1pNjd1dLXzIsD7mcKg4g0hhyWBzJmXGYIGJVwCB+jURg9TAmEIwmadsWbRjC0dee+gSZ0d3aCG/UEkjSdvMTLHk+VYOIDnt5TPsdo/WqjXa7eVLpKMuLwpohsHd3aZMBr4IFkHCkTKHDBJlMmqBWabgBuW9sJJy2SJh4gkG1efCVnYGXzIFe6191ZDRiIRRjLXBSQxbw2aQDkjDg7WJHephC46hcrTf6fX8cvqfvFKEbluENY4M3/+YwQWyBLLB3IGBAAJ9hKB6DiRLE8XSeVZeZwTA/LstuwVTR9qHaqdfCDItoGgHF1O03iz1V12UJtNbhyUm934fMlWVL9r673qjKomy4Va91JQdkd54gQFyxNYZ2IdWfZu0UBvALT7oQXXctBM8B2V+nJgzV6AA/6rV4JIxIMh2z3yyaJIG5ShkMRxNp2Gr1ZVi6Ztn2q/cfyEJVZyOpdv3UowSBgQ2qDPjbqyEAFUeVPl5vNVLJJDIec9lJAKgxHkEWq9SGerej0XDgo/pSmI5NIZLM1E5Puk7o9M6PIC91E/5BtyAAmYJ9ZNypIrJMVyOgpIJyBRoV2K9ECeKe/AH4Fsxu+UlXkBN61Ol0Wk6YpmFh+IduU9/VQ0++67yHi3OtRS/LZocS37rBSmcslNShEV2HJTLh1WVOvSEGrLlmJ9foMxnyQQbuOppg341GoxPw84svMYTch5tPkOvozvp2H5azzr267y6CXD3mlT87JJyXZbvzzfLKYF054T7cHMnbvXI9/6OPwGuFgE+Q16o7/ZtxGgGfIE4j6rf3WiHgE+S16k7/ZpxGwCeI04j67b1WCPgEea26078ZpxHwCeI0on57rxUCc/hBXisc/JvxEbgVAX8EuRUW/0cfgTMEfIL4T4KPwAwEfILMAMff5SPgE8R/BnwEZiDgE2QGOP4uHwGfIP4z4CMwAwGfIDPA8Xf5CPgE8Z8BH4EZCPgEmQGOv8tHwCeI/wz4CMxAwCfIDHD8XT4CPkH8Z8BHYAYCPkFmgOPv8hHwCeI/Az4CMxDwCTIDHH+Xj4BPEP8Z8BGYgYBPkBng+Lt8BHyC+M+Aj8AMBHyCzADH3+Uj4BPEfwZ8BGYg4BNkBjj+Lh8BnyD+M+AjMAMBnyAzwPF3+Qj4BPGfAR+BGQj4BJkBjr/LR8AniP8M+AjMQMAnyAxw/F0+Aj5B/GfAR2AGAj5BZoDj7/IR8AniPwM+AjMQ8AkyAxx/l4+ATxD/GfARmIGAT5AZ4Pi7fAR8gvjPgI/ADAR8gswAx9/lI+ATxH8GfARmIOATZAY4/i4fAZ8g/jPgIzADAZ8gM8Dxd/kIPJggx9h33UDvl7HP3vjl/OuLh95+nGO/vnhBX7aXAtcH7gWYHkyQF1qa64efxs43cq5mFnJy7yf/8BYX+fhPWQtpfd5Gv/B7i9zGf/7leZtZ0Pn/97dnmfyn/s2CWp+r2X/wTXzwG3783k4l5rqIYye/84PTpn7l3/4Bx5p0rKF/9pnct5SaP/cn/80/daxJ5xr67/5m4tsS+//qX/zj73SuTeda+m9/uPjpRPs3v/Qp55p0qqU/9rPp7+R+6TP//h/d06BHCPL221M5fwfyp++R14Xdjz7/B+Gqf+0b/sXPf7sLV599yebfynw9jiC//C1/2YsE+Qc//Cd+3H7AjNk34cben//ZjV+PIvof/sff9m2zLz+nirX3A9+QYla/u3p+kS//nkjoU785/WL8r98cDvy2H7t3CLsq3rtfzjs4gjgl2ydsfiCpP2N96aqs8312SrZj8xuBH8jv5NvzyXPtbKeEU//SypQfCH6t+bm+OCXbL6DfH0UQ4q9YP3qPOHMS5Od+ovSd3/vWT36sPr3Mlz/B/tk/8P98/Ffhi/4H/+zwj3239T3/1T3Xv7b7x9E/iV77Ya4vzsqGkIiDo61Tsm1Rv94FkH5l9Mm5oLp+slPC/VL7O9Av/NDfc3R+5JRsDWTNvul15N/p12/+5rc5O/2//D57Vv3FT/3VH7Mb/sUf/QyCfP7T37WDIH/1//rev4Mi1p/6h3/kD12/5A9eocAnfue1ffI/wf/raz/M98VR2RDjp1EHNWmnZIv+0Pe9+W3x/c//vr8/H1bXznZKuN9AqY++Cw/Bf/bPE9fan+eLU7IlkCNbjENEP9yeLY/1wK2M/onnZ35kAz5/Cd2e/vIJ7FcsM54z7C8D7L+wrGuHohf2Kvj7P0+Pv/znH6Hfevl5rg/XLuiMbNb3o39oLpkuTnZatl+IAZDbP3vR/Hx/nRXuMyjx9q+J734K/Zb5pDo721nZ/gm61bMs7VtR7MuzhZtzBEE+99Nf7cMcjJ6y8OPTfz/xK1/5+G5v+6/YXyz26fS35/+Yzz/e/PQT6Hff/Gmu707K9vf+9ps/M5cwN052SrYf+h/+3H+TefYD3/lbf/3GBeb56pBwJkJ+voi89XOPfvn/+8Z5xLl2rkOy/dHP/eKbn2a+2Cid3DPJmJMgf/5Hcp/Ks8g/rExvIj39N2MNkS6y99mz+xKv3d6sL0/+ffH3z9r/qvuclO1H/9yHvhh5VQFmHO+UbL/8A9/xNxHknZ/f/lt/ZnXG5V5tl1PCRZCPFuHK7O/7qV93jCBOyYZ9/m9/7meYb/m570BSs8GZjyDt/+Ujv8bBBf63s4s0p38aaBgJI9/+z2+/8N1zEGen6IiTsv3d7/vIF53ToxHnZPvX6CdsmNmP/cJXVu0PTmyOAfcIOXunRBHJCbnsNhyTDcH/4l+E9pS9xMps2eYjyKH5SZsfp4dnF/l/p3/+LfJR5HHky8btxr3PXpmko1cn6crn8O+aLeur7XVQtr/x3/+2XwKjoHObY7IpyJl9t41QjknnmHC/G30yFerdM4uREwI6Jtu5MD+r3us/mj1FuXvvdM7UQL8J5uKjT6EYHPglFPtR+PML6CP49y+jn5Hsk+tPbkzS7R9v3X4G/fStvz/gR4dl+yz6sf4DpLj9FGdl+6dotgrX+T8wDqac82/OCmd9Gvs7INMvYnFhftHOTD3OPXBTkb6SSNTvEW2+EST9R//3d37v8JfYd746ZeSn/sL/+fbez7M/BV/+x6/9+Od/V76196t/7Y2XfHP8BOqsF90x2X76fyL+0x+xb2L1j7/krdx7mGOy/ZFPfvGNb888+QLyN5wb4RwTDvmx3/r+L3z08F8SP8nfi8hLHuCcbJ9kP8Q//ULg85n7rnwPge7cXca+C/ZJf2mLLX1P7xM4fP4S9tkvfzIMnvSzcz73e+J04eN//RRGkOmhd7Z0tuMpumLec8hL73ZWth+8MEw7Y610Fjf9R745TKa/9Ysvjc3MA50FzrI637tKJ7/jN2Ze82V3OizbD//2KLPxPfbwO3tDrfsY5O/3EfgAI3CPFfgDjIx/6z4CgIBPEP8x8BGYgYBPkBng+Lt8BHyC+M+Aj8AMBHyCzADH3+UjMIcf5IpL3DkcHTKqeVk2xMvCeVm2hQB33wPnjyDOkdtv6TVEwCfIa9ip/i05h4BPEOew9Ft6DRHwCfIadqp/S84h4BPEOSz9ll5DBHyCvIad6t+ScwjMYeZ1Tgi/pfcrAihO0HwowDBmuz3U9RkFB96vd+gT5P3ac56QG6PocKGQjkb1d98zZMkniCd6xRfCMwigJBtdfXMzl1UZoWNpmmcEc0wQfwRxDMoPXEMYRYZy2XypFCMmo5GsG6/hAOJkOc0P3APyQb9hnA1m33yjyPOUMmo3BUW/L2zj/QiYP4K8H3vNGzLjbDj35u9Y1XWx2ao0R8rrOEf/oI4gJMPSFEWSBEHgYOk2NVUaDETL4zoCilLRGI9hZq8nGC4rNDhNR0vF1ZTZ7vd73U6zOtR8gnjjxeWEFPCgRUJ8kOUYhoFBVBPF1sG+AiWMnGh8YW2gOLeyXSRJbWfnSFXdZTMRDBfefCOLC42Tk+Z4NOoPNcPb6D2sWz6gKhYVyWWTqWg4zAeDUHJN7veP0G4PMT3dxVD3m1v57R9iGJkdNxHDXZMRwSdX3vpY9LR6+PTpqa7pMKJ5GryH0QOWEHnoie/X81AU4/hQulhKhiMBiiZxBCMpjuSQoRpotQSv3dZZMXzI0rAsE2OZdDaXx3GBQqFEkouiojgeLW5tZiipubdXbg7M13LwmAL8wSMIQURXSsV8IUxRiK5pypgN8gSDWwoXf1f2HkEIe6IE0yTL0PFQKJ+Oh3QdxFZdVfjBPxhff2clqAyOnu53x15XTed5lXzgCIIRVGzj7bVcjjUMcTBRUJSzaJINcEw2IZ/MA+VCzkUJmqYp6CWwIxCxRC6dCIsi8MNdguA0l1h/Jy8IDSCIrntwDULHOmNJBCE4lkQRjMBJmoE1qSzdUHr9kWN38QoNBVLJja2VBDkRhNFwMFCgNHo8Hk/EmRApFUrDyQR+cXnDIMCJZUkcB82K4C7sCEPBTMSzUY6wNEVzdYKOImwqs5Hi1FZlt9KduKnr3dZTKIbSDM1xHGYbKBFRFCfiw8vLL4kgVCwVxFCCoYPRaBCWPVXkwdOnrhAktP7GaiFLjju9eqMnjmGmy0Qj+cd4CmcjuQ250XCfIDjDhpNJnqKgf6kAxwFZEGRSr02i0XSYsgxV1i0MXUj2+G0P3Au/oWig8HgjprcP3t0/Eb3GDwTeL5FoJJNJk6T9Lq7X6s2G9wkSX0mgGMUHY/l8DJb4HI9rVrPyAvRL+CG0/rFSKDxqV8v7Bx1FgSUc6VBog8gkCLBsbarWcLAEIWZfAmeCqbW1OMfC24sKBoEi0NHC/n43HIqHaURXZM3CMNcIgqJ4MP/WCgsE+a39yWT2vbiwF5TocDa3tbUJJnwgyM7THVxsPViOxY4gmD3DxOGfxOYGEITkAuF0KgwjyGRCbves/rK1LJwi4+l8AlX7p7tHldMz3xYhjajTU7D3MrGC2AVM3d6IQDRd3EhOPTQEDB8wWJAkntFjXIAPU4bYbQwmLgY+UTwPVo7IoHFcbgy8Nv8gCDISieTyuZWVAkXbi6ZIOg7O4PFo/LBuXSxBiEAQVGguGEyursTgzUNRTMgW2g4C3UJiz54tWcuCvk3Go8xYrB4+Oe2LZ64tU0UG9TKbJelwVix7giDxTGElSdrGK4wgDHFsBYN4qKhRFB2kjFGj0h6r7hlW2XR+u5RCu88OjgXda84PKhAolorpNETgM9MpCBIqsEwgVKl4kiBkIBbm+Ug8li4UwigC0ydQEKcEwfGteB5tHz+M1g89i+QTQBCi3wP3VgPye6b6s6lq/UaZo2KBMCrHzpYjfegFHDmPDMYz+ZXk2SzDQsZiU01YIT4KahWG46rQPGmPVMQ13Z9Jb22vpAadd58JgufiE6lgdPVDb4HZBYKIzgAMsZlAKEqOHmihdH4EAdcWeBYozLbBcNFoOBgMRWDSxNrK1uXzg6EUj6phezRZ5oaRDIWZ8qBebw+ery5qgie9Fk4qtgrIkLBY1jJFunktiuXy65ur6RAlQ3isqU7EQbOpJRLJRDKAoqhlKcNmtSe5JyPKxIv5oCkO2h1ZdU+Mm7jZ3+EFHC0Wt9dXgnxAmaiwgY2cptiEYlYDD1zow3mCYASZLK2EKBoUBDoIKhbLshyFySbLPicIAg5tHF/6RBMGMXjk5FatOYLZ+fNNH7WiQw0AxnGCcNctzGbSq9tbxSStDvoTHQRrdQYDMxbPbuMMjL6GLg2btaH8XPYlf8JQJppPEKPucAzGtCVf/J7LgT6a2nqrWIwSqDKETRjGorFwhKKCMZ62Vwm85/zbdi+AICSdfPTR9NQGA14usLXBHEkfizJ6bbyAGQmxdIIAL01VHLerNwkikOGhCmMf8IN0V69mQYHZ3k4w1HhQ66tq9+CwNZHQaGSFSCRgWDY0adCqq+ptfbmM3+DFRkdzyQkQRJQf9MQtUEqMoJOb/0k0FgU326DRbDZa+ULBoMNU0ACCPGy65DBBwGIVikW3Hm8mp+MFUBoHrXlKZisU4sDSGwgwsKSZHTqhLj+BwJDH7UNivFdpj655f01tIkF0LAAcjMVE0U1XCMGGo7EYjxjj5kFLUbqVSt8waB4DvyGGWJowPG0NRNM1RyEZCGTiYXJcO+54LwM9FE9sruQoXOpBAH6r0253JtLE5JIYzQcCrKZd0xpekqgOEwTM9vmV1dJKlqdIYME01A4d7+/VwRIDMhJkqFBMkPYuS51AluZLSunUYdoYBY+b3GwNxtcvDaGAloUSSCCWybd1Nwliq3ngQ9f0wenuqSKPeyMdJ8PZ1Y1siEQtpXN6WB+5GKnIJNOrCVbr7O+33ETp9gci8Wj7USGkiMr+/v54uhnSgIqrFokEgqEJcr3Pb2/j5q9OE4RPrH7kw1nQ+1AgAZTjtv8bH/zqgSwjDM0wdEpigog9F7E0IMi11/hN0RbwXR0roxqtyxKEZ19tHvgBAzCKY1w029FHboYsAkFg1EUMuV/dPZRlVdNQkolk1zezIVCQgdkAABiGSURBVMo05M7RTm18Tfar97H4z0xyfTXJjtr7+23vEST+6JszGV4Tu09//TcMCMDXgR+t2IqGEAQX5K0H5Qc4TBAmXlpbXY3TDHDAskkgqbpxtLt/pGkWRJVHIxEDTA0IPI+TzvFBc9luWFPVZbi6cfMJs8A7LUsKTNG5cLTjMCav9tTq8kgQJdKAKEWWmsDMDWWjicL6WiFK6aLYPoI5yXP726s17cTRVCSbYjShW2+OHvS8OSHDnW3QoWQkQGrDeu305OwgHDHHignWv+l254kzdjj8MHDpjZVUkIbARNhMS6w3B5NJdac1hmdSVzEqkYvzDIFDgRjh5Ml75WW/qmGkgBHtxbmlpUmQFDemGdvSS18xts1AbkG71EE9nBwQOBXJP0IrqoHhofzq2vpKmkMnrcbpzl554J4JC0HIYCKEjnoQabr04f9+wC3TztqSerXe9dgry7Ds+Oebr8X7G4QjnCfIagosBjZB4Gkc13Zrg0Gn3hbhoUQRmkpkEzyDYqYuCyfvfUVYNkEQc8rcF8x9pmqII2FkkSjFBih3CTJshLPDAMdGCwqidiYwbcs/3irkI4g1ah7u7+yfuFp9CggSRkbdXl9wz1Bw54N9TpB+rX/tHQL2XUgWeGAItJMEwUkiksylQtP0BdOUx+OT3af1gTAUxLPh2CSDkQANlzSlYbtVr7vQ1S9wY4o2vGJst5JhorbdbUqiO3thwTt0sdfp9XgSZeOK2K5ZPJ/b3lhJROiR0Crv7YMTfcECzGgewOGjca4/OG6f9ygKyVzw+BnuBb5cE3fq6tAngqTDVA6jKAgtCsVoHbSD0778sBwBJwkCJqxUMh5i7Om5qWmdyulxudyVJeliNIbpJniqYa8hdho9iCdyzVp5DVb7C0zSXbQNXRFHl9BBrxdhQ0QglkqluHy+AOnBQVLrnh7v7pZvKA9XTlzCRyLAJeJxfLC73zyfoeNcgDR0TVm6PfKuu4WXm2moBgJhf1Q0Eg2FwysBuVapPKtJ7qtYVCieSsRBwQLpTVVu732t3GiO4fVyQQSYeF4hiKi+OBm4674X/TsYFIAftw8vi7729fYNSR/0e4mwSQWsUSode+NxKRIJ4ITcLT/b26+qbk6NyUAEopwmg73dzrkOA4HHjKpJyINcDNdv3JFvwA/U1GGswCiOy4BiChsQ5N2vtpqy+45CNl7MxYMMjoKBTRwMDvd3TwaDc58veGBBAYuEeQY3YW+/1Rx4ytEE5mjY7HnTw3B0pHvtRkzdlCADTjNxBo0XRHN7K8swmDru1I7LtfbQses8pCEylE4GCU1oNEUdFBiaYYPxGKOqYqczVNSHeBkeIsXd5+DTIhwQWGJgLM8H87kcKMzipLazB26RB4rnpIrFpTaKUQpUeH0y6VRrR4fN4QUJoAwGxGUVsulYgDAlqdtqtgTXwiVuQRj4C9t0fHY30gRGMgTeLyAEuC2ja5yVjsOgqw/71ZNqc+gyZHQkl6Tl0djmLxWJJNKZONQd1XThuFLr9R4YT35Lbzz0J4hCgCQzPitmR+NQKBwJRzrtLqTcVk+hLuoDG3WUIOmNYsy2YGnioLa/VzltjC48DjD5ZaPxQjYVo0lTHnbaXiOI7b8GhsB8U3d5wmmLYK+0AQn8UTaLwKwNxcC0f1w5bbr9TqGi+STU4R1BmKJJRnJrj7azLIMbRv9JkEQn7hOE5EIsaQWzGAAIuhVFEkr5oNlqSZJ0oeW/Mk2cJAhEliAGJCpYg1q9vL9f744una04ZyfbrhViHGpI7dpJudqClAbPbChOgsJAg0UGQhnPJpygcAFlIKASQnjMpZo0LVMHfcVCcYRgbYSgSMOwcXxYbQ3cngrT0UIc6YByDGHuTHxlLQvdqekYzqUVxBQh9thlo4ttukIsJkaAKhAK8/Ak4mIdctLnec6cJIjUOcYxBgIiant7p7X6YPLcM0NG0/n1tVI2hEF20tHOfr3eOTcUziO8Y+dCnkgAkr9pzJh0z5kLXnWIjeFYCqqfqMpSe95Upas2SUjgb1T2D+tDye0HkI4V+H79sC1bCMakNvLGoSFJEGPH4NmAMVHH42vuB8d656UbMnUVJ0wiYBvqodAFzCeViTCZz6zhMEFIJkTr+umzr9QFUPueE4SKFjYfP86xLKpPeuWvPhUEUX+oVvjSeL38gSjFBIAhUBZa7LUFG1GwKdBBSIYMBxpNRFxumU9Dk6Esw6X0uggT9IPD/uCBdpjLhub+AATB6mUgiImhbGojXjmp97pQjCi3sVEU+wKquk8QgjRJnAWCQDybPRRPRtJ8z5mTBJG74GQzSV0/2jvsXHnrgn8pkl3bWi8lEUSZ9Oonx8fTciJzd5gzDdh1lFhYaI+FfBVIludVi4UZCU0x4XAoEmbBs9kxrocuOHPdO1uBtJSzdGo4wtSNQa1yeHjSBBXmzjOWsQPMGBwfVfV+T9TBP8hyAVw4Puh2jGRCiq4F4rmh6fYsZFzfC0L2PoFDUXJbNZVlQRAk74wgysBU+xUw43ZOBFCiL3uNCPKFtUdQpgOEVgfV4xYMLkvVWS4lueUDTDVIgoMpHWe/K8jUNtnrTSDsmKZonofVKXEYWSi1f8uZi/oJhUz+M2ngCpBpVn/69LDREper5r14c7DeAeigtiUcVHuG4/S+UKs2xpI5MIhkQkXSmtx58ayl/tL4qggvNYqkCvk8XFjq95vdh9uvzkR3cgRRBmKvwthp0xNRv+J1g0LghbXtNWC1ZWn9ahkI4rKz4Uq3gX2NYvhwLMrZIVhUyoz2+jKMJ2DmB+UaQfVAMKAss+dRlAhGI1NpQCBj3Kk8/Q8HsuR62AHBwCQNtHt7ERWcDbFGX69XG7oGYRFGIo6iGaZ9dAVXNz42J0dRqGbCsBI7JUiv1uoJ5nNF/yEyOUkQXb9dE6ETa1ur+SSKwTqorWr5uCW6pyzY7kDweNgGqqkeA4YP8LnysSgYqAE/IoLz8YESCgXAcQjFqKC49ZKXvSChSHAmEw+cR0waithvVE4e0rUOn4PRUD4WswwNAIFh1xjWZLBTwkV0GW9UqHQ6GHO7ZNJoVLNVAQ4KB9s3L7aO6nZt2bk2JwlylyBc8Z03SkHwU6vNRuUplHN1bS5n224hkhxK7kEliYCtVAFXaBgjAlysEAD5ISUY3uAKy2BjyLyFCFBV6XY7p0ustsjn85uPS6ngMjrmrg679XfIR6YJ1FTGEwWsQ2PMGmvt847UB1WGZ8iAB2RWRga80s7s4UL1WXXu0M5l3BMQ5M1AACK0lNbu7t5+TZmX1bd24Ev9iMFiIGSABzdryB6MYVYO0/EQzxIEFMGFFlAKZiS0QuDGpNns9QeqoggjYbLEFKVg6cPbqyuR5zH3V3TVl7rFRR0EBIEoCUMWRdU0FHAGt43heUdqgyq9QkOu0qKu/fLtqqYMI9yZ5WpU3Zk/9nnhBIGwymR+tTSdsmu9yn6l0Xv523XySJSA2ZsdQMQAP2AkjsbjDEiFQUZ/CLQrsAyaGiy8oSmiXeBdqZ7W2q0exMBDMLKTYsxoy862JVKrb2xkEoxpaKYJxiK7FJEd/On+Zo+9QBB1ImmWpZrqGIMkizOxDBHjJcLlTLNzUQyFZO36AtC1UP9sfs/MwgnCJJKbKZjs2gyBFOGeS/5BUK6g5kUoxE+rpbNQMh2sU4TtIregnp0F9ABlGqqv9AfD0RimUnpv0IeYCt3QIXZ6ORtYw4Ph0PZaEfQrQ1YUWSHCYaA15RGGTFEA2ymYIMHDAHmskEVzhoylg+PGM3ZJkgtDte8H1ol7oasXT5D0xlYKTFu2wOYECOLSBB3DwNacSSag0iMF70Kof0WQCKz9Ypi4XUvRZog+rFer1cZwCKnypj0BgUR6eBaW1fPgmoxks1trxQSFazKwc0wjHA4jsB054ZUNckEVGEFMywChLrQ/O6XfWwShzh44B1BbOEHoWBHqDYCkpqr2+r2hS+4uWFClsPEon0rxEF4H3Qs5cJBBqEHREJ2I2FN1MF9K3eOD4+P6UFh2LYmzfiSYQKK4sVlMcqoodHuCOOGDCTBmnb+mHejseZuwOQHpUWCjv6DGRYt2poA3Es7ABk0xsPIkhHxqhgPYLZggYBJiQzyDAaRyu3Vw3BGXprFc9N3Z33CxuLq+FueDmDSGmYY0HsN7UFNgU0NbmymKRjVteLqz025DyNP1U5f1jeRjufXHpTgpdTrNel3CibSomwpY05al5N1zp3cOZDgXsqNQLzSue5pZ9G47M920fXFDiM2Z++2ycIJA7IadhGshSnPvWbkrPjjueD5cwxtvr+bzHEFIY2EiyVB5WTLteoqSLGcUnkdJDGp6VnfehUI7bgWJQT2E/PobKY6E4gxHx8daNEpNDDCojuVlKXmzIbb5cTtHMDYS4zCXE2kuhbcJYiHysC94nyCwRCsfiYVZHEKKhhDjWxsqc1P6EohX+sAXPlSMxy0JchlbgjTpNZtQvxMsMqqhK1sTqCOnjyAnqVxxa2EaKFrHJ4srxWJAFRrlHVD1cDNgT9ugvLtLg+7L40sEE0lGG3thpAM3MEXDmkMGxPx1x9qVgKeXv51rRy5yBIFC4OFQKQ9uYUIbjU4qJ7WBW/xAKD4ZwmGVg2q70xVVdSwAVS3QtYhYaD0fplFdrBztlYeuvQUpli1svbmRhKq3dQjdbQgIn4FVuWhTk+yqwV7Zbnu9oQgkUuVJKDXhnn/rEh8w+iVyq3FKgbD8Wk+cPw94gQSBkA42lgGCxChCGrZOTiq1sWsQUqFk2FAaT75e7/cUAyz49hJNMK8Mx1e28rBOiT6qfO3p6dC1Fb+hIFJ+88O5CNktv3t4WoXqonx2cz1JS0AQjxiI7tTnMTKSzxNgInetdy/5gcCC3vHcWoJSBrXDahdU1Oe7HvZpcQSxa/wkC6W1XDygS/165aBS67hQCOscFqgpyo2V7uFXq8LwchIOwRPRwqPNXAiXJvWjZzvD8dx4PqwXIIyYT2RL6zHcGlTePeh0zAAXThZyHDmBkjreIMg0FRjq6dA0JM1fuU2wQ8eS2cSgXXcvhOhSHJSLx4ulUsAatKpHjbkDsaDdRREEkixCmXS+WCpkGLnTrZXLx6fCkuP+LmE7+wDOacifES+1PBTls5mVzY1CnFFqp4d7DUG62vE3zl7wV1AB40EamajtZr0z1ik+ngxDjVZwxkHNzNsUmwXL82LzBrhmJpIFJRH6/ec4oQiTShVX4+SoUm64Yx5/LipEoWa2tksraal3enhcHdweO/v8+Jf5tDiC4KHSo9ViAWrxjpoHB4eHDfBL3zlKv4yo8x1jgWVck0Uo8ng+SoAGGFx5c7NYitF0v/bus+PG0C37FdwZEARWQbIkod1sdDWIsU+mLgniDfMprK0C5UyQYFY0ntcagFAdJrO9Xoyjo5O9vtsEgWiY9FvfGItHK73KwfGpIzF/CyMIPq0pm80GLGvS2H16VO65+h6cGsftwCaStP3AdtIPRWXXP7yZTDKK1D5++rTbcbN/QQWEihyGIgwGPYEgAol8LspghgpFtccXhSnne0PMe7ahoMJwgAcKqiqArQ9C3kFLADhTpa21ID7qnlbcW/fq/N7A0pHfeociSbW5c1R1JolnUQSBMgh8PJfiCahBOmiUK71LzWbejnrY+YYyxkkwDA0ESEtRQG3R2GisuL0SxcVeq3X8pNJ7aGWxh8nz4lk2aae/olBBrLT11gosMzBSTo8OT7xgHQKDs64Ma/tRegNKTzXGIwNM+DTUaIumCgW6W27sdVXX00TDmcxaklVEuXyw13Qo/3eBBAnGsymagBqkw0b5WHLZwqHLIkuxmQ1NIggo7QMOwlCpmMtkGFVs7e2WT6u9+S2CLz70r/qLTRFYBDCcKG1/KEfRyqB3BOnokicKJJmaMazvr6RLMVQnW7rKMlwgmCiVUoGAXH521OiqrqeJhkuP1pKM2O+UD3YnDukDiyEIZONxoWg8EbUseTRs1KrNV31UnD5eHTawMBUqYDpFQSr/WJxE1tcTLKeNe+UnXysLLq4be36rUye1ZWIMz2Zz6xtbUWkybFT3yyeuY3cmIMSvCXWGjEeDokzQqAJ5mKFQemM9poKKsLMjuV20C+aUsZW3SjFK7Z3A5tQDtBCC4BTUwUqXopSpat3y8U5liQlHdwDTfYIUs1kcSUFQuRaTZEUlg1pL1UadTr1SH7lovzoTGDLjIUcKCjsxJQPPZIo5RqpWa9Xaac0JU8wdoLzqz3IXMlbQAL6W2Ohr9hLkDMMJ4CCs77Xdt0VDkmg2X4ozhtg97ToH2mIIQnOx/EopRsIks7X31f2qQ8Pdq3boleO77zVWNzbS8TQNxa/sKHcDFl3rdOwaqH1hJEHgp7sbxLqTUDMJCLKS4FLJWJjpVd89rNU6I+f6eu47VDoTHTXzyTVKUS1I6UdhOfJB67hS7/XkpWUF3HUXdDiaKZQYIEin6uAiEQshCMlF0kUoZE0YsOr9wVfLE/c7eTg8bAmySqdpiMy2yzMgQh0qEFWqLVeX/LvsbbCvTfOirECcDsVjlGkOT5/uNBoOTTUvrzPXB1Udapim83wCyGFP2/Ver32wu1sHu+BcDc9/MooGErlCLqPr417r1OsEYdOl1Y01WLdY7taOm1C6y234ph0g1tHByRP6IiBVGgx7nU7fbePV+bOhy+OJatERyyBJqN46FoTD43rXreyyux9YpYtpgzIPgwcQxDTEVqvRmLjPD4Igcm883oohEIv67kHNERfhGQgLGUFYiLNbXYmyhNI9hjJx8vwRMXf32MvvEeuj04BdTueMIpAMAlW/ZS8Yr+AewE8NCwrRYdqyi8ybo0Ztv1zvuZRdNgNTpQtpZTxlr+5tl5BTJxNYXsD18QMCYJjs42+Mx5De/s7BQV12zma6AIKgCJdcXS8WWMsUO5VK+6rfdQb0C98lSe2FX+PBF9AnsJKjTHNBSE2G8Kt+dX+/0hxcC3p6cNtOnqiqSyyB9PKCUxyf334HxfTOwX+s11ovf+K9RzpPEDBhBRmKwFB1NDquNvui2/PfezHwwgFyj4Zc4HQkgioqLLhbg+U6W7CohSd0Uy/gM0sGuyBHJpsLk6AR1Gu1vqMzXucJQjBckLUrcSi9xvFpo3dlEYRZd/kB3yf3dBw3xyUGHY/7nW5lb68FZW99frzMYwEekHB+Ix8hhWG3VqvKjpZpWgBB2HAoYK/VKXcrsE5O33t69MuAvuxjlP7YQlQN57E+BI7Xj/f33V2OcNn3P8/1MIKM5rdzIUzuwbI0DvtVnScIHctCklSUUgf1o3KjD4Uq57n5D8q5UGJ5VNfF1gEqihCw2O7NV7T/gwLb9D65cHh1fSNsdStPn+07XZbQeYIwsUIxn+VRddA4KncGUJjjA9VbD7xZyzAFfdg4CEJ1FXBhQiL6Axv6AJ4WSObW1tcNs3v85De7Ti9U4TxBqHAml0mTY7FfL1dGY/9N+FKPrGUgY085BV9Kak8chAbTqyulIoRFHD/7j4479J0nCIQ3wcRcq1efnfYnHqo34InO9IVwGgGotpxdfcwLTxv1xl7H+VXunSeIOmr3JqZYe7pz0vf1K6efB7+9GwhQQT679lixi4Kc1LoOVIq70f4CCCK0u2N1VH2657BF+obk/lcfAUCADNhxf+Vm+ejoSJKcn+86TxBtjO2Tg/HuEdQe8LvQR2DBCKhjZA/ttlvtVgeqADp/sTmqxF9E/d0QiqTpCCzq2O2OpQckUzvkGrtDthuivuJXh2S7o37nKwpz83CHhHvfAQdrvsTjcVG0Y8IgieEmLPd+vw835wlyr0gzD7hP3pknP9/5vuvn56I/8JMP3MOAuw83b6xe9LB788/yEVg4AnOMIAuXzb+Aj4DrCPgjiOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUEfIJ4uXd82VxHwCeI613gC+BlBHyCeLl3fNlcR8AniOtd4AvgZQR8gni5d3zZXEfAJ4jrXeAL4GUE/n/HY3ufK2ycdQAAAABJRU5ErkJggg=="
    }
   },
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The classification problem: Automatically detect numbers written in a check\n",
    "![image.png](attachment:image.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "(x_raw, y_raw), (x_raw_test, y_raw_test), min_, max_ = load_mnist(raw=True)\n",
    "\n",
    "# Random Selection:\n",
    "n_train = np.shape(x_raw)[0]\n",
    "num_selection = 7500\n",
    "random_selection_indices = np.random.choice(n_train, num_selection)\n",
    "x_raw = x_raw[random_selection_indices]\n",
    "y_raw = y_raw[random_selection_indices]\n",
    "\n",
    "BACKDOOR_TYPE = \"pattern\" # one of ['pattern', 'pixel', 'image']"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Adversary's goal: make some easy money "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"../utils/data/images/zero_to_one.png\" width=400>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import HTML\n",
    "HTML('<img src=\"../utils/data/images/zero_to_one.png\" width=400>')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_val = np.max(x_raw)\n",
    "def add_modification(x):\n",
    "        if BACKDOOR_TYPE == 'pattern':\n",
    "            return add_pattern_bd(x, pixel_value=max_val)\n",
    "        elif BACKDOOR_TYPE == 'pixel':\n",
    "            return add_single_bd(x, pixel_value=max_val) \n",
    "        elif BACKDOOR_TYPE == 'image':\n",
    "            return insert_image(x, backdoor_path='../utils/data/backdoors/alert.png', size=(10,10))\n",
    "        else:\n",
    "            raise(\"Unknown backdoor type\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def poison_dataset(x_clean, y_clean, percent_poison, poison_func):\n",
    "    x_poison = np.copy(x_clean)\n",
    "    y_poison = np.copy(y_clean)\n",
    "    is_poison = np.zeros(np.shape(y_poison))\n",
    "    \n",
    "    # sources=np.arange(10) # 0, 1, 2, 3, ...\n",
    "    # targets=(np.arange(10) + 1) % 10 # 1, 2, 3, 4, ...\n",
    "    sources = np.array([0])\n",
    "    targets = np.array([1])\n",
    "    for i, (src, tgt) in enumerate(zip(sources, targets)):\n",
    "        n_points_in_tgt = np.size(np.where(y_clean == tgt))\n",
    "        num_poison = round((percent_poison * n_points_in_tgt) / (1 - percent_poison))\n",
    "        src_imgs = x_clean[y_clean == src]\n",
    "\n",
    "        n_points_in_src = np.shape(src_imgs)[0]\n",
    "        indices_to_be_poisoned = np.random.choice(n_points_in_src, num_poison)\n",
    "\n",
    "        imgs_to_be_poisoned = np.copy(src_imgs[indices_to_be_poisoned])\n",
    "        backdoor_attack = PoisoningAttackBackdoor(poison_func)\n",
    "        imgs_to_be_poisoned, poison_labels = backdoor_attack.poison(imgs_to_be_poisoned, y=np.ones(num_poison) * tgt)\n",
    "        x_poison = np.append(x_poison, imgs_to_be_poisoned, axis=0)\n",
    "        y_poison = np.append(y_poison, poison_labels, axis=0)\n",
    "        is_poison = np.append(is_poison, np.ones(num_poison))\n",
    "\n",
    "    is_poison = is_poison != 0\n",
    "\n",
    "    return is_poison, x_poison, y_poison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Poison training data\n",
    "percent_poison = .33\n",
    "(is_poison_train, x_poisoned_raw, y_poisoned_raw) = poison_dataset(x_raw, y_raw, percent_poison, add_modification)\n",
    "x_train, y_train = preprocess(x_poisoned_raw, y_poisoned_raw)\n",
    "# Add channel axis:\n",
    "x_train = np.expand_dims(x_train, axis=3)\n",
    "\n",
    "# Poison test data\n",
    "(is_poison_test, x_poisoned_raw_test, y_poisoned_raw_test) = poison_dataset(x_raw_test, y_raw_test, percent_poison, add_modification)\n",
    "x_test, y_test = preprocess(x_poisoned_raw_test, y_poisoned_raw_test)\n",
    "# Add channel axis:\n",
    "x_test = np.expand_dims(x_test, axis=3)\n",
    "\n",
    "# Shuffle training data\n",
    "n_train = np.shape(y_train)[0]\n",
    "shuffled_indices = np.arange(n_train)\n",
    "np.random.shuffle(shuffled_indices)\n",
    "x_train = x_train[shuffled_indices]\n",
    "y_train = y_train[shuffled_indices]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Victim bank trains a neural network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:74: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:517: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4138: The name tf.random_uniform is deprecated. Please use tf.random.uniform instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:3976: The name tf.nn.max_pool is deprecated. Please use tf.nn.max_pool2d instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:133: The name tf.placeholder_with_default is deprecated. Please use tf.compat.v1.placeholder_with_default instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:3445: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/optimizers.py:790: The name tf.train.Optimizer is deprecated. Please use tf.compat.v1.train.Optimizer instead.\n",
      "\n",
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:3295: The name tf.log is deprecated. Please use tf.math.log instead.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Create Keras convolutional neural network - basic architecture from Keras examples\n",
    "# Source here: https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py\n",
    "\n",
    "model = Sequential()\n",
    "model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=x_train.shape[1:]))\n",
    "model.add(Conv2D(64, (3, 3), activation='relu'))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Dropout(0.25))\n",
    "model.add(Flatten())\n",
    "model.add(Dense(128, activation='relu'))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(10, activation='softmax'))\n",
    "\n",
    "model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /Users/ebubechuba/anaconda3/envs/art/lib/python3.6/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.where in 2.0, which has the same broadcast rule as np.where\n",
      "Epoch 1/3\n",
      "61/61 [==============================] - 8s 132ms/step - loss: 0.8180 - acc: 0.7331\n",
      "Epoch 2/3\n",
      "61/61 [==============================] - 7s 115ms/step - loss: 0.2557 - acc: 0.9264\n",
      "Epoch 3/3\n",
      "61/61 [==============================] - 7s 116ms/step - loss: 0.1681 - acc: 0.9539\n"
     ]
    }
   ],
   "source": [
    "classifier = KerasClassifier(model=model, clip_values=(min_, max_))\n",
    "classifier.fit(x_train, y_train, nb_epochs=3, batch_size=128)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The victim bank evaluates the model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Evaluation on clean test samples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Clean test set accuracy: 96.47%\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction: 0\n"
     ]
    }
   ],
   "source": [
    "clean_x_test = x_test[is_poison_test == 0]\n",
    "clean_y_test = y_test[is_poison_test == 0]\n",
    "\n",
    "clean_preds = np.argmax(classifier.predict(clean_x_test), axis=1)\n",
    "clean_correct = np.sum(clean_preds == np.argmax(clean_y_test, axis=1))\n",
    "clean_total = clean_y_test.shape[0]\n",
    "\n",
    "clean_acc = clean_correct / clean_total\n",
    "print(\"\\nClean test set accuracy: %.2f%%\" % (clean_acc * 100))\n",
    "\n",
    "# Display image, label, and prediction for a clean sample to show how the poisoned model classifies a clean sample\n",
    "\n",
    "c = 0 # class to display\n",
    "i = 0 # image of the class to display\n",
    "\n",
    "c_idx = np.where(np.argmax(clean_y_test,1) == c)[0][i] # index of the image in clean arrays\n",
    "\n",
    "plt.imshow(clean_x_test[c_idx].squeeze())\n",
    "plt.show()\n",
    "clean_label = c\n",
    "print(\"Prediction: \" + str(clean_preds[c_idx]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### But the adversary has other plans..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAD4CAYAAAAq5pAIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAOg0lEQVR4nO3dbYxc5XnG8euyY5zW4GJDcA04BFJveEmEqbZACokgqECoFOADFEehhJJsGkFFpEgNIh+g6hda8iKUIpoFu5goISEKLoigguWkdVEjw5oabDDBQFxs7NoJTmNDhbG9dz/sUG3MzjPrOWdevPf/J61m9txzzrk98rVnZp5z5nFECMDUN63XDQDoDsIOJEHYgSQIO5AEYQeSeE83d3aYZ8Z7NaubuwRSeUtv6u3Y44lqlcJu+2JJd0iaLumeiLit9Pj3apbO8gVVdgmgYHWsbFpr+2W87emS7pT0SUmnSlps+9R2twegs6q8Zz9T0ksR8UpEvC3p+5IuractAHWrEvbjJG0e9/uWxrLfYnvI9ojtkb3aU2F3AKqoEvaJPgR417m3ETEcEYMRMThDMyvsDkAVVcK+RdKCcb8fL2lrtXYAdEqVsD8laaHtE20fJukqSQ/X0xaAurU99BYR+2zfIOkxjQ29LY2I52rrDECtKo2zR8Sjkh6tqRcAHcTpskAShB1IgrADSRB2IAnCDiRB2IEkCDuQBGEHkiDsQBKEHUiCsANJEHYgCcIOJEHYgSQIO5AEYQeSIOxAEoQdSIKwA0kQdiAJwg4k0dUpm9GeXZ8+u1hfdfudbW97aPN5xfrqH3+kWN9z1Gix/sIVzXsbeHyouO7AtWuKdRwcjuxAEoQdSIKwA0kQdiAJwg4kQdiBJAg7kATj7FPAqMpj3SXDC/61vO2//Enb25ZU7OyR8/+huO71jy8u1mddt69Y37d5S7GeTaWw294kabek/ZL2RcRgHU0BqF8dR/bzI+JXNWwHQAfxnh1IomrYQ9LjttfYnvBEZ9tDtkdsj+zVnoq7A9Cuqi/jz4mIrbaPkbTC9gsRsWr8AyJiWNKwJM323Ki4PwBtqnRkj4itjdsdkpZLOrOOpgDUr+2w255l+4h37ku6UNL6uhoDUK8qL+PnSVpu+53tfC8i/qWWrpKZPu+YYn3O51/tUifdNTDjsGL9sdN+WKxftOSKYn3mhQfd0pTWdtgj4hVJp9fYC4AOYugNSIKwA0kQdiAJwg4kQdiBJLjEtQ9sGvqDYv0/B+7o2L6ve/X8Yv2JjeXeNlzw7TrbOShXHf9Usb7chSHNyHcyJ0d2IAnCDiRB2IEkCDuQBGEHkiDsQBKEHUiCcfZuGLsMuKnZH93RsV1fu6l8nefr5/y6WF+op4v1T+mPytv/3Eeb1n72N+Wvkm7l2t/bVKwvfeSPm9bm/OnGSvs+FHFkB5Ig7EAShB1IgrADSRB2IAnCDiRB2IEkGGfvA/92+v2V1t+yr/m0Wi9/++TiukfqZ5X23cpR9zTf/smnXV9c9/krv1Vp3/++6HtNa63OD5iKOLIDSRB2IAnCDiRB2IEkCDuQBGEHkiDsQBKMs3fBe44/rqPbX/Hmh5rWjryvs+PoVRy+qXys2T36drF+xLTylM8lv7it+XX2knTSLeXr+GNP83Mb+lXLI7vtpbZ32F4/btlc2ytsb2zczulsmwCqmszL+HslXXzAspskrYyIhZJWNn4H0Mdahj0iVknaecDiSyUta9xfJumymvsCULN2P6CbFxHbJKlx23RSLdtDtkdsj+zVofc+B5gqOv5pfEQMR8RgRAzO0MxO7w5AE+2Gfbvt+ZLUuO3c16MCqEW7YX9Y0jWN+9dIeqiedgB0iqPFPNW275d0nqSjJW2XdIukf5b0gKT3S3pV0hURceCHeO8y23PjLF9QseVDz4tLB4v1Fy66q9L2T3nwhqa1hX+1utK2e+nKDf9drH9m9ua2tz2txXHuU5d8plgffWZD2/vupNWxUrti54QTFbQ8qSYiFjcp5UstcAjjdFkgCcIOJEHYgSQIO5AEYQeS4BLXGkyf1/RsYUnSx055sVhvNQy0/M25xfrC7/xvsX6ouudvy5dc/Pntd7a97YHHh8r1Z9a0ve1+xZEdSIKwA0kQdiAJwg4kQdiBJAg7kARhB5JgnL0Go8e+r1i/+/33ltdvsf0lmz9WfsCT61ps4dA055Hni/VrbyhfeLnkhBXNizHhVaBTGkd2IAnCDiRB2IEkCDuQBGEHkiDsQBKEHUiCcfYa/PyLv9vrFqak/bt2Fes79xzb9rb/7twfFuvLTjq/WN/3yqa2990rHNmBJAg7kARhB5Ig7EAShB1IgrADSRB2IAnG2WtwysLXet0CDtLls8ozjP/TEb/TpU66p+WR3fZS2ztsrx+37Fbbr9le2/i5pLNtAqhqMi/j75V08QTLvxkRixo/j9bbFoC6tQx7RKySVH7NA6DvVfmA7gbbzzZe5s9p9iDbQ7ZHbI/s1Z4KuwNQRbthv0vSByUtkrRN0tebPTAihiNiMCIGZ2hmm7sDUFVbYY+I7RGxPyJGJd0t6cx62wJQt7bCbnv+uF8vl7S+2WMB9IeW4+y275d0nqSjbW+RdIuk82wvkhSSNkn6Qgd77HvTHOV6xXOX3GL75erUVeV5v/31U8vrvl6+lr7Vd/33o5Zhj4jFEyxe0oFeAHQQp8sCSRB2IAnCDiRB2IEkCDuQBJe41mC0xfS/oxUHamKKTi88/bQPFeu/Pr3pWdiSpHNnry7WS8/73SPlabAHtowU64cijuxAEoQdSIKwA0kQdiAJwg4kQdiBJAg7kATj7Khk+sKTyg8Yfqtp6cJjyuPkXzxyYzstTcrAP+b7ijSO7EAShB1IgrADSRB2IAnCDiRB2IEkCDuQBOPsKHr5a2cX6zNP3F2sPz3wg6a1Vl+x3dGva35yXSe33pc4sgNJEHYgCcIOJEHYgSQIO5AEYQeSIOxAEoyz1+Cl1ScU69MGqv1NfeTkh8oPeK39bc/w9GJ9b6xpf+OSSseT1vsub/m0VX9RrJ+4+JnyBpJp+b/Q9gLbP7W9wfZztm9sLJ9re4XtjY3b8jf6A+ipyRxy9kn6ckScIulsSdfbPlXSTZJWRsRCSSsbvwPoUy3DHhHbIuLpxv3dkjZIOk7SpZKWNR62TNJlnWoSQHUH9WbS9gcknSFptaR5EbFNGvuDIOmYJusM2R6xPbJX+b73C+gXkw677cMl/UjSlyJi12TXi4jhiBiMiMEZmtlOjwBqMKmw256hsaB/NyIebCzebnt+oz5f0o7OtAigDi2H3mxb0hJJGyLiG+NKD0u6RtJtjdsW40NT10k/KL/QGb26oxdrVtJqeKvqdNMlD7wxt1j/yk/+rFg/5a9fKNb3H3RHU9tkxtnPkXS1pHW21zaW3ayxkD9g+zpJr0q6ojMtAqhDy7BHxBOS3KR8Qb3tAOgUTpcFkiDsQBKEHUiCsANJEHYgCS5xrcG0X2wp1j+y6nPF+rqP31NnO131m9G3i/UHdw80rS3/7CeK6w48+WSxzjj6weHIDiRB2IEkCDuQBGEHkiDsQBKEHUiCsANJMM5eg/3/85ti/cRPP1usn/HVG4v1twbeKtY3XPDtYr3kulfPL9ZHfvzhYv2w8j9d8771H4VqvmmTe4kjO5AEYQeSIOxAEoQdSIKwA0kQdiAJwg4k4YgWXxxeo9meG2eZL6QFSh7burZYv+jYRU1rq2OldsXOCb8NmiM7kARhB5Ig7EAShB1IgrADSRB2IAnCDiQxmfnZF0i6T9LvSxqVNBwRd9i+VdLnJf2y8dCbI+LRTjUKTCWlsfTSOHoVk/nyin2SvhwRT9s+QtIa2ysatW9GxNc60hmAWk1mfvZtkrY17u+2vUHScZ1uDEC9Duo9u+0PSDpD0urGohtsP2t7qe05TdYZsj1ie2Sv9lRqFkD7Jh1224dL+pGkL0XELkl3SfqgpEUaO/J/faL1ImI4IgYjYnCGZtbQMoB2TCrstmdoLOjfjYgHJSkitkfE/ogYlXS3pDM71yaAqlqG3bYlLZG0ISK+MW75/HEPu1zS+vrbA1CXyXwaf46kqyWts/3OeMHNkhbbXiQpJG2S9IWOdAgcgqpcplpl3ZLJfBr/hKSJro9lTB04hHAGHZAEYQeSIOxAEoQdSIKwA0kQdiAJpmwGOqDKZaqdusSVIzuQBGEHkiDsQBKEHUiCsANJEHYgCcIOJNHVKZtt/1LSf41bdLSkX3WtgYPTr731a18SvbWrzt5OiIj3TVToatjftXN7JCIGe9ZAQb/21q99SfTWrm71xst4IAnCDiTR67AP93j/Jf3aW7/2JdFbu7rSW0/fswPonl4f2QF0CWEHkuhJ2G1fbPvntl+yfVMvemjG9ibb62yvtT3S416W2t5he/24ZXNtr7C9sXE74Rx7PertVtuvNZ67tbYv6VFvC2z/1PYG28/ZvrGxvKfPXaGvrjxvXX/Pbnu6pBcl/YmkLZKekrQ4Ip7vaiNN2N4kaTAien4Chu2PS3pD0n0R8eHGsr+XtDMibmv8oZwTEV/pk95ulfRGr6fxbsxWNH/8NOOSLpP0WfXwuSv0daW68Lz14sh+pqSXIuKViHhb0vclXdqDPvpeRKyStPOAxZdKWta4v0xj/1m6rklvfSEitkXE0437uyW9M814T5+7Ql9d0YuwHydp87jft6i/5nsPSY/bXmN7qNfNTGBeRGyTxv7zSDqmx/0cqOU03t10wDTjffPctTP9eVW9CPtEU0n10/jfORHxh5I+Ken6xstVTM6kpvHulgmmGe8L7U5/XlUvwr5F0oJxvx8vaWsP+phQRGxt3O6QtFz9NxX19ndm0G3c7uhxP/+vn6bxnmiacfXBc9fL6c97EfanJC20faLtwyRdJenhHvTxLrZnNT44ke1Zki5U/01F/bCkaxr3r5H0UA97+S39Mo13s2nG1ePnrufTn0dE138kXaKxT+RflvTVXvTQpK+TJD3T+Hmu171Jul9jL+v2auwV0XWSjpK0UtLGxu3cPurtO5LWSXpWY8Ga36PeztXYW8NnJa1t/FzS6+eu0FdXnjdOlwWS4Aw6IAnCDiRB2IEkCDuQBGEHkiDsQBKEHUji/wAMzEe1QCF2zgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction: 1\n",
      "\n",
      " Effectiveness of poison: 100.00%\n"
     ]
    }
   ],
   "source": [
    "poison_x_test = x_test[is_poison_test]\n",
    "poison_y_test = y_test[is_poison_test]\n",
    "\n",
    "poison_preds = np.argmax(classifier.predict(poison_x_test), axis=1)\n",
    "poison_correct = np.sum(poison_preds == np.argmax(poison_y_test, axis=1))\n",
    "poison_total = poison_y_test.shape[0]\n",
    "\n",
    "# Display image, label, and prediction for a poisoned image to see the backdoor working\n",
    "\n",
    "c = 1 # class to display\n",
    "i = 0 # image of the class to display\n",
    "\n",
    "c_idx = np.where(np.argmax(poison_y_test,1) == c)[0][i] # index of the image in poison arrays\n",
    "\n",
    "plt.imshow(poison_x_test[c_idx].squeeze())\n",
    "plt.show()\n",
    "poison_label = c\n",
    "print(\"Prediction: \" + str(poison_preds[c_idx]))\n",
    "\n",
    "poison_acc = poison_correct / poison_total\n",
    "print(\"\\n Effectiveness of poison: %.2f%%\" % (poison_acc * 100))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Evaluate accuracy on entire test set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Overall test set accuracy: 96.66%\n"
     ]
    }
   ],
   "source": [
    "total_correct = clean_correct + poison_correct\n",
    "total = clean_total + poison_total\n",
    "\n",
    "total_acc = total_correct / total\n",
    "print(\"\\n Overall test set accuracy: %.2f%%\" % (total_acc * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- # Detect Poison Using Activation Defence -->\n",
    "<!-- ![image.png](attachment:image.png) -->"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "cleanse = NeuralCleanse(classifier)\n",
    "defence_cleanse = cleanse(classifier, steps=10, learning_rate=0.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Identifying the Backdoor\n",
    "\n",
    "Unlike most defenses, part of the procedure for this defense is identifying exactly what the suspected backdoor is for each class. Below is the reverse-engineered backdoor. This will be appended to clean images to mimic backdoor behavior"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generating backdoor for class 1: 100%|██████████| 10/10 [01:14<00:00,  7.46s/it]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7feb9433c3c8>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAD4CAYAAAAq5pAIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAUGUlEQVR4nO3dbYxc1XkH8P9/Zt/wev2yGJvFNuCAEbihMbAxEFIUSgOGNAKqpsFSKa1QHDUgkSoqpUQqqO0HhJLQfGgjmWBhUgpKSyioIiGOC6KorcMajLFjbC/GL2sb29iYXdvsy8w8/bBDuoE9zx3PnZk79vn/pNXszjPn3rN355k7O88959DMICKnvlzWHRCRxlCyi0RCyS4SCSW7SCSU7CKRaGnkztrYbh3obOQuK0f68RRVCyZs+2SuiDCXcL5oa61+46NjbthKpao3zRb/qW+FQtXbztIwjmHURiZ9wqVKdpJLAXwfQB7AD83sQe/xHejE5bw2/IBc3t9hqXjCfawUW9vcuI2NVr3tXEeHGy8ND1e97azlpna5cc47Mxws+S9yNrDPjZeOHXPjnvys2W68uP9A1dvO0lpbE4xV/TaeZB7APwK4AcAiAMtILqp2eyJSX2n+Z18CoN/MtpvZKICnANxUm26JSK2lSfa5AHZP+HmgfN9vILmcZB/JvjGMpNidiKSRJtkn+xDgE/+EmdkKM+s1s95WtKfYnYikkSbZBwDMn/DzPAB703VHROolTbK/CmAhyQUk2wDcCuC52nRLRGqt6tKbmRVI3gXgBYyX3laa2aY0nWFrQu1zpPrSW8v8eW58+II5brz1cLg8ltv9rr/zpJptQj25niXHRAnXCJSGhtx4PndWMHbsvOlu2yk7B9x4klxXuCx4spbWAPh/E6eamarObmbPA3g+zTZEpDF0uaxIJJTsIpFQsotEQskuEgklu0gklOwikWjoeHaS7nBPzuvx2xfD45ft0PtuWzt+3I13bPFr5aPnnhGM5bpnuG1LSfViq35cdlpJ1x8UdqerdRc3bQnGOhKuykh7VLxrAEqfX+y2zb2y3o0fue1KNz7jR//jxlOpcv4DndlFIqFkF4mEkl0kEkp2kUgo2UUioWQXiURDS28Gf9pkHjritz+jO9x2pj9c0jr8WXIKW/rdeG5gTzBWrOM01GnlZ53uxrf/2dlu/Oy/TVd6y9KRPwmXx2Y87pfGzvjvhHLqPR+68a2PfNaNX/C1V924y5uF2RkNrTO7SCSU7CKRULKLRELJLhIJJbtIJJTsIpFQsotEoqF1dpjBRsJLQBWdGADg/fAw1vz5C9ymxYQ6eirNvORywjTVw/MTprFuYvkZ/rUVXi39onX+U3/zZf41H6v3PubGe//mz914GrnTnGHix8Pnb53ZRSKhZBeJhJJdJBJKdpFIKNlFIqFkF4mEkl0kEo2ts9dRcfsuN86WhOWgSwm18iyXTU7h6DUXuvGp2+r7FMgvuiAYK/5qa6ptF4984Mbtc58JxjZf9obbNjdlihu//ix/KurZiw65ce/ZlPRc5VnO8uI7w21T/aVJ7gAwhPG+F8ysN832RKR+avGyfo2ZvVeD7YhIHel/dpFIpE12A/BzkutILp/sASSXk+wj2TeGhGvfRaRu0r6Nv8rM9pKcDWA1ybfM7OWJDzCzFQBWAMA0djfxiBGRU1uqM7uZ7S3fHgDwDIAlteiUiNRe1clOspNk10ffA7gOwMZadUxEaivN2/g5AJ7h+JzpLQD+xcx+VpNeBXjLPZeGhxMaJ/yqJ2kdPclpz/7SjZf+8PJU2891dbnxtLV0T0vPmf4D9hwOhuz08BoEAFA8FG5biTS/d36uv3S5HXbG2hfDz+Oqk93MtgMIX7UgIk1FpTeRSCjZRSKhZBeJhJJdJBJKdpFINNUQ13xCOYTTp4VjQ8fctmlLKaeqzn9b68YLv3tZqu23/Oe6VO09pcEhN56bFX4+NfPzobBzt/8AZ8lmc0pvOrOLRELJLhIJJbtIJJTsIpFQsotEQskuEgklu0gkmqrO7tUPAfjLD+foNs1Pm+rGk6Ylrie7yp+WuHXnQX8Dba3BUGH7jip69P/2/k67Gz/3Gb9eXUq1d1/pmH9tRW7OGcFYfs5st21x/4Gq+tQQVQ7H1pldJBJKdpFIKNlFIqFkF4mEkl0kEkp2kUgo2UUi0VR1dkuom6JQCMfyCa9bzthmAMgnxIv97/jbd4xe7y9uW+zw+966I2k56eqr2Uk1/rFp/rZ3fck/bvM2nHCXaibVNQb0r9uAnXyLG+nMLhIJJbtIJJTsIpFQsotEQskuEgklu0gklOwikWiqOntivdiZE7t01K/RW8I84Ulz1rvLA7e3uW1H3ShwaJH/ZxiZfo4bP/2FtxP2EJZf95Yb//uVW9z4Qw/fWvW+s5Rf+Ck3PjJ/phuv53z49ZJ4Zie5kuQBkhsn3NdNcjXJbeVb/8iISOYqeRv/GIClH7vvXgBrzGwhgDXln0WkiSUmu5m9DODj74FvArCq/P0qADfXuF8iUmPVfkA3x8z2AUD5NjihF8nlJPtI9o1hpMrdiUhadf803sxWmFmvmfW2wp+8UETqp9pk30+yBwDKt008FaeIANUn+3MAbi9/fzuAZ2vTHRGpl8Q6O8knAXwBwCySAwDuB/AggB+TvAPALgBfqUVnSt688ABYDNfhbSyhmp0wJz1b/ENh3dPDwTFnnD2A3df52566ww1j6oD/u6WZ49wuXujG//qVT/sbuNT/3cMzt2eLR4+78YFr5rjxs8cuceO5/3r9hPtUqVxHRzDG4fA4/MRkN7NlgdC1ib0Skaahy2VFIqFkF4mEkl0kEkp2kUgo2UUi0WRDXP2laM2JJ5bOvGmoAQzcep4bn/tkfzBWmu8v/zsroQrTccgvOba/Ft43AFS3gO+4/Lvvu/Ff/N5TbvwP/uGeFHvPTmlwyI2fdcVeNz5w4TQ3vuANP14cHHTjntLwcDBmzhTXOrOLRELJLhIJJbtIJJTsIpFQsotEQskuEgklu0gkmqvOnkJSHb1w7WVufEa/337H8vODsQWP7XLbjlzh11xnbPGnwS4e+cCNp7H3y2e78aVP/qUb73nHP27NiufMdeNH/n2WG/+Pex5y43eONN+gUJ3ZRSKhZBeJhJJdJBJKdpFIKNlFIqFkF4mEkl0kEo2vs3tTOieMZ/e0zPPrpljjL7H71c3+dMxPLwpPLbz3zivdtvnh8BhjACi1+K+54cmB0+v5121u/OCXw9cXAEDndn9cdsIi3Jmx7f61EUPf7nTjD+3/ohs/+vu/7cY7n17rxj3e8uE8GE5pndlFIqFkF4mEkl0kEkp2kUgo2UUioWQXiYSSXSQSja+zW30qr4WBPW585IbPuvGn7vZr4cNfDR+qnhcPuW3f/8xMN54bS5gv342mU1wQrtkCQNtRf+/FrvDywUB9rxFIg11dbnzGS/7v9XdX/8KN3/b0hyfcp4/kz1/gxgtv7wjGrBSeXyDxzE5yJckDJDdOuO8BkntIri9/3Zi0HRHJViVv4x8DsHSS+x82s8Xlr+dr2y0RqbXEZDezlwEcbkBfRKSO0nxAdxfJDeW3+cF/SkkuJ9lHsm8MIyl2JyJpVJvsPwBwHoDFAPYB+G7ogWa2wsx6zay3Fe1V7k5E0qoq2c1sv5kVzawE4BEAS2rbLRGptaqSnWTPhB9vAbAx9FgRaQ6JdXaSTwL4AoBZJAcA3A/gCyQXY7wEvAPA1+vYx5po/+mrbnxw2RVuvG0ofH2A0a8md+4bdeOWMJ69nvJHjrvx1mN+vbml31/HPM3a8Unyp3f7D/DmTkgweK0/l/+VL93lxs/H61Xvu+jU0QGg5czw3AreePbEZDezZZPc/WhSOxFpLrpcViQSSnaRSCjZRSKhZBeJhJJdJBKnzJLNac3YdMSNF6eGr/7jiF9aaxlMKAHl/Nfceg5xfWdZuIwDADPf8ockd7a21rI7J6Yl4ek7Er48u3jhOW5T9vtTSdv8+l36ffxm/xq1Kc+Ep6E2SzHEVURODUp2kUgo2UUioWQXiYSSXSQSSnaRSCjZRSKRwVTS9awaV6+04S03PrY0PBX1kYV+rXp0uj8EtnXIPybd/uhc18iX/Cm0p/f7dfRSQhk9aQrvVBKGDhf3+8tse3Lrt7rx8/fMcuOlIx/48YT9t5wzPxjresuf8rHU2hYOjoWPmc7sIpFQsotEQskuEgklu0gklOwikVCyi0RCyS4SiYbW2Uki1xGemrjkjD9OVOf6PUvh7edH/X2z4NeLTzucbsJlb4nf9p++5rYd+OeL3fjCh/yx+mkW4PamRAYAm+4vq1zc0l/1vkvDw35890DV265EYefuYIxeHR2AFcacYPi5qDO7SCSU7CKRULKLRELJLhIJJbtIJJTsIpFQsotEorHj2XM5sCM8/zpGnfohAJTquQCwrzAl/LrYetyvNlvCtPFdG/b7+/abo9j/TjC29Z/8Ocgvuv89Nz508Rlu/LTOxW78+Jnhv/fwDP9cM3PLh248t8UNn7Tys/2x9HY8fFz4QfjJlnhmJzmf5IskN5PcRPLu8v3dJFeT3Fa+nZm0LRHJTiVv4wsAvmVmFwG4AsCdJBcBuBfAGjNbCGBN+WcRaVKJyW5m+8zstfL3QwA2A5gL4CYAq8oPWwXg5np1UkTSO6EP6EieC+ASAGsBzDGzfcD4CwKA2YE2y0n2kewbNf9/MBGpn4qTneRUAE8D+KaZDVbazsxWmFmvmfW28bRq+igiNVBRspNsxXiiP2FmPynfvZ9kTzneA6D6qT5FpO4SS28kCeBRAJvN7HsTQs8BuB3Ag+XbZxP3RgLt4VJMrs0f4loaDpfeclOm+PvO+/Wv0m+Fh4kCQOtgeN/tu/ypfzF0zA0XUkyJnOSCb/zSjQ/dcrkbH5viD889fLV/3EvOaM1Cpz80uPOAP9QzPFj65JY0Rbb/XA8f00rq7FcBuA3AmyTXl++7D+NJ/mOSdwDYBeArFWxLRDKSmOxm9gqA0Mv7tbXtjojUiy6XFYmEkl0kEkp2kUgo2UUioWQXiURjh7haCfgwPIWvFaufmLh0/Lgb3/b4pW6863W/altyjlR3pz8MtP29aW48f9Svw5eO+XFP0rTEnTuOuvEt3/Dr6Lkhf/+lmeFhy/nD/nrQrUMJg3tzCWOHMxwSncbPdvW58evPCg8rNgvnkM7sIpFQsotEQskuEgklu0gklOwikVCyi0RCyS4SiYbW2a1YQtGpKTNhzLln6NYr3DgThpz/8R0vuPGXll4YjI0s9Jcefm+xX6vueWeqG09TZ7cxf8nl/NvhpYMB4KLvTDrb2K8dWexPe9wyHP6bDs/0x8q3rtvmxktOTbnZvbB3fTDm1dEB4N2/+FwwNvbE/wZjOrOLRELJLhIJJbtIJJTsIpFQsotEQskuEgklu0gkaObP3V1L09htl1MT0n7CKTouO62ksfj5Of48AoWBPVXvO2kdgqT5E7w6OuDX0tO0XWtrMGiHJ72AQWd2kUgo2UUioWQXiYSSXSQSSnaRSCjZRSKhZBeJRCXrs88H8DiAMwGUAKwws++TfADA1wAcLD/0PjN7vl4dPaVFWkdPkjQWv7j/oBv3sMV/6tezjp7UPk3bJdeH+13J5BUFAN8ys9dIdgFYR3J1OfawmX2ngm2ISMYqWZ99H4B95e+HSG4GMLfeHROR2jqh/9lJngvgEgBry3fdRXIDyZUkZwbaLCfZR7JvDCOpOisi1as42UlOBfA0gG+a2SCAHwA4D8BijJ/5vztZOzNbYWa9ZtbbivYadFlEqlFRspNsxXiiP2FmPwEAM9tvZkUbX0nuEQBL6tdNEUkrMdlJEsCjADab2fcm3N8z4WG3ANhY++6JSK1U8mn8VQBuA/AmyY8+878PwDKSiwEYgB0Avl6XHjaJ/LTwssvFwUG3ba6z042nmSo6ZlYILwed3NZfDvqet99042nKY0nt07TdaoeCsUo+jX8FwGTjY1VTFzmJ6Ao6kUgo2UUioWQXiYSSXSQSSnaRSCjZRSLRXFNJp5lSOaFt0nLQ7Ei4lHcsXNMtjSRc89/AY3xKob+kM1ta3XjSENlTkaaSFhElu0gslOwikVCyi0RCyS4SCSW7SCSU7CKRaGidneRBADsn3DULwHsN68CJada+NWu/APWtWrXs2zlmNula1g1N9k/snOwzs97MOuBo1r41a78A9a1ajeqb3saLRELJLhKJrJN9Rcb79zRr35q1X4D6Vq2G9C3T/9lFpHGyPrOLSIMo2UUikUmyk1xKcgvJfpL3ZtGHEJI7SL5Jcj3Jvoz7spLkAZIbJ9zXTXI1yW3l20nX2Muobw+Q3FM+dutJ3phR3+aTfJHkZpKbSN5dvj/TY+f0qyHHreH/s5PMA9gK4IsABgC8CmCZmf2qoR0JILkDQK+ZZX4BBsmrARwF8LiZfbp830MADpvZg+UXyplm9ldN0rcHABzNehnv8mpFPROXGQdwM4A/RYbHzunXH6EBxy2LM/sSAP1mtt3MRgE8BeCmDPrR9MzsZQCHP3b3TQBWlb9fhfEnS8MF+tYUzGyfmb1W/n4IwEfLjGd67Jx+NUQWyT4XwO4JPw+gudZ7NwA/J7mO5PKsOzOJOWa2Dxh/8gCYnXF/Pi5xGe9G+tgy401z7KpZ/jytLJJ9svmxmqn+d5WZXQrgBgB3lt+uSmUqWsa7USZZZrwpVLv8eVpZJPsAgPkTfp4HYG8G/ZiUme0t3x4A8Ayabynq/R+toFu+PZBxf36tmZbxnmyZcTTBscty+fMskv1VAAtJLiDZBuBWAM9l0I9PINlZ/uAEJDsBXIfmW4r6OQC3l7+/HcCzGfblNzTLMt6hZcaR8bHLfPlzM2v4F4AbMf6J/NsAvp1FHwL9+hSAN8pfm7LuG4AnMf62bgzj74juAHA6gDUAtpVvu5uobz8C8CaADRhPrJ6M+vZ5jP9ruAHA+vLXjVkfO6dfDTluulxWJBK6gk4kEkp2kUgo2UUioWQXiYSSXSQSSnaRSCjZRSLxf/HjQnXJQgiqAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "pattern, mask = defence_cleanse.generate_backdoor(x_test, y_test, np.array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]))\n",
    "plt.imshow(np.squeeze(mask * pattern))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Usually `generate_backdoor` is called as a result of calling `mitigate`. During this process, this defense generates a suspected backdoor for each class visualized above. The `mitigate` method also performs the mitigation  types presented below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Mitigation Types\n",
    "\n",
    "There are different mitigation methods that are described below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtering\n",
    "\n",
    "Filtering is the process of abstaining from potentially poisonous predictions at runtime. When this method is set, neurons are ranked by their association with the backdoor, and when neural activations are higher than normal, the classifier abstains from predication (output is all zeros)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generating backdoor for class 0: 100%|██████████| 10/10 [01:10<00:00,  7.04s/it]\n",
      "Generating backdoor for class 1: 100%|██████████| 10/10 [01:09<00:00,  6.95s/it]\n",
      "Generating backdoor for class 2: 100%|██████████| 10/10 [01:09<00:00,  7.00s/it]\n",
      "Generating backdoor for class 3: 100%|██████████| 10/10 [01:10<00:00,  7.02s/it]\n",
      "Generating backdoor for class 4: 100%|██████████| 10/10 [01:09<00:00,  6.95s/it]\n",
      "Generating backdoor for class 5: 100%|██████████| 10/10 [01:09<00:00,  6.96s/it]\n",
      "Generating backdoor for class 6: 100%|██████████| 10/10 [01:09<00:00,  6.97s/it]\n",
      "Generating backdoor for class 7: 100%|██████████| 10/10 [01:11<00:00,  7.16s/it]\n",
      "Generating backdoor for class 8: 100%|██████████| 10/10 [01:14<00:00,  7.43s/it]\n",
      "Generating backdoor for class 9: 100%|██████████| 10/10 [01:11<00:00,  7.17s/it]\n"
     ]
    }
   ],
   "source": [
    "defence_cleanse = cleanse(classifier, steps=10, learning_rate=0.1)\n",
    "defence_cleanse.mitigate(clean_x_test, clean_y_test, mitigation_types=[\"filtering\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtered 500/559 poison samples (89.45% effective)\n"
     ]
    }
   ],
   "source": [
    "poison_pred = defence_cleanse.predict(poison_x_test)\n",
    "num_filtered = np.sum(np.all(poison_pred == np.zeros(10), axis=1))\n",
    "num_poison = len(poison_pred)\n",
    "effectiveness = float(num_filtered) / num_poison * 100\n",
    "print(\"Filtered {}/{} poison samples ({:.2f}% effective)\".format(num_filtered, num_poison, effectiveness))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Unlearning\n",
    "\n",
    "Unlearning is the process of retraining the backdoors with the correct label for one epoch. This works best for Trojan-style triggers that react to a specific neuron configuration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generating backdoor for class 0: 100%|██████████| 10/10 [01:16<00:00,  7.68s/it]\n",
      "Generating backdoor for class 1: 100%|██████████| 10/10 [01:11<00:00,  7.17s/it]\n",
      "Generating backdoor for class 2: 100%|██████████| 10/10 [01:13<00:00,  7.39s/it]\n",
      "Generating backdoor for class 3: 100%|██████████| 10/10 [01:12<00:00,  7.24s/it]\n",
      "Generating backdoor for class 4: 100%|██████████| 10/10 [01:12<00:00,  7.28s/it]\n",
      "Generating backdoor for class 5: 100%|██████████| 10/10 [01:09<00:00,  7.00s/it]\n",
      "Generating backdoor for class 6: 100%|██████████| 10/10 [01:10<00:00,  7.01s/it]\n",
      "Generating backdoor for class 7: 100%|██████████| 10/10 [01:11<00:00,  7.18s/it]\n",
      "Generating backdoor for class 8: 100%|██████████| 10/10 [01:11<00:00,  7.13s/it]\n",
      "Generating backdoor for class 9: 100%|██████████| 10/10 [01:10<00:00,  7.01s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/1\n",
      "   5/4129 [..............................] - ETA: 1:03 - loss: 2.7731 - acc: 0.6000    "
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4129/4129 [==============================] - 70s 17ms/step - loss: 0.0115 - acc: 0.9981\n"
     ]
    }
   ],
   "source": [
    "defence_cleanse = cleanse(classifier, steps=10, learning_rate=0.1)\n",
    "defence_cleanse.mitigate(clean_x_test, clean_y_test, mitigation_types=[\"unlearning\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Effectiveness of poison after unlearning: 5.19% (previously 100.00%)\n",
      "\n",
      " Clean test set accuracy: 54.14% (previously 96.47%)\n"
     ]
    }
   ],
   "source": [
    "poison_preds = np.argmax(classifier.predict(poison_x_test), axis=1)\n",
    "poison_correct = np.sum(poison_preds == np.argmax(poison_y_test, axis=1))\n",
    "poison_total = poison_y_test.shape[0]\n",
    "new_poison_acc = poison_correct / poison_total\n",
    "print(\"\\n Effectiveness of poison after unlearning: %.2f%% (previously %.2f%%)\" % (new_poison_acc * 100, poison_acc * 100))\n",
    "clean_preds = np.argmax(classifier.predict(clean_x_test), axis=1)\n",
    "clean_correct = np.sum(clean_preds == np.argmax(clean_y_test, axis=1))\n",
    "clean_total = clean_y_test.shape[0]\n",
    "\n",
    "new_clean_acc = clean_correct / clean_total\n",
    "print(\"\\n Clean test set accuracy: %.2f%% (previously %.2f%%)\" % (new_clean_acc * 100, clean_acc * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pruning\n",
    "\n",
    "Pruning is the process of zero-ing out neurons strongly associated with backdoor behavior until the backdoor is ineffective or 30% of all neurons have been pruned. Be careful as this can negatively affect the accuracy of your model. This works best for fully mitigating the effects of backdoor poisoning attacks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generating backdoor for class 0: 100%|██████████| 10/10 [01:15<00:00,  7.57s/it]\n",
      "Generating backdoor for class 1: 100%|██████████| 10/10 [01:14<00:00,  7.49s/it]\n",
      "Generating backdoor for class 2: 100%|██████████| 10/10 [01:14<00:00,  7.49s/it]\n",
      "Generating backdoor for class 3: 100%|██████████| 10/10 [01:16<00:00,  7.61s/it]\n",
      "Generating backdoor for class 4: 100%|██████████| 10/10 [01:15<00:00,  7.51s/it]\n",
      "Generating backdoor for class 5: 100%|██████████| 10/10 [01:11<00:00,  7.17s/it]\n",
      "Generating backdoor for class 6: 100%|██████████| 10/10 [01:11<00:00,  7.19s/it]\n",
      "Generating backdoor for class 7: 100%|██████████| 10/10 [01:11<00:00,  7.18s/it]\n",
      "Generating backdoor for class 8: 100%|██████████| 10/10 [01:10<00:00,  7.06s/it]\n",
      "Generating backdoor for class 9: 100%|██████████| 10/10 [01:10<00:00,  7.02s/it]\n"
     ]
    }
   ],
   "source": [
    "defence_cleanse = cleanse(classifier, steps=10, learning_rate=0.1)\n",
    "defence_cleanse.mitigate(clean_x_test, clean_y_test, mitigation_types=[\"pruning\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " Effectiveness of poison after pruning: 0.00% (previously 100.00%)\n",
      "\n",
      " Clean test set accuracy: 46.46% (previously 96.47%)\n"
     ]
    }
   ],
   "source": [
    "poison_preds = np.argmax(classifier.predict(poison_x_test), axis=1)\n",
    "poison_correct = np.sum(poison_preds == np.argmax(poison_y_test, axis=1))\n",
    "poison_total = poison_y_test.shape[0]\n",
    "new_poison_acc = poison_correct / poison_total\n",
    "print(\"\\n Effectiveness of poison after pruning: %.2f%% (previously %.2f%%)\" % (new_poison_acc * 100, poison_acc * 100))\n",
    "clean_preds = np.argmax(classifier.predict(clean_x_test), axis=1)\n",
    "clean_correct = np.sum(clean_preds == np.argmax(clean_y_test, axis=1))\n",
    "clean_total = clean_y_test.shape[0]\n",
    "\n",
    "new_clean_acc = clean_correct / clean_total\n",
    "print(\"\\n Clean test set accuracy: %.2f%% (previously %.2f%%)\" % (new_clean_acc * 100, clean_acc * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Combination\n",
    "\n",
    "Finally, you can also do a combination of any of the above mitigation methods to fit your needs. Just add those types to the `mitigation_types` list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Generating backdoor for class 0: 100%|██████████| 10/10 [01:12<00:00,  7.25s/it]\n",
      "Generating backdoor for class 1: 100%|██████████| 10/10 [01:13<00:00,  7.38s/it]\n",
      "Generating backdoor for class 2: 100%|██████████| 10/10 [01:10<00:00,  7.07s/it]\n",
      "Generating backdoor for class 3: 100%|██████████| 10/10 [01:10<00:00,  7.04s/it]\n",
      "Generating backdoor for class 4: 100%|██████████| 10/10 [01:13<00:00,  7.35s/it]\n",
      "Generating backdoor for class 5: 100%|██████████| 10/10 [01:10<00:00,  7.07s/it]\n",
      "Generating backdoor for class 6: 100%|██████████| 10/10 [01:10<00:00,  7.04s/it]\n",
      "Generating backdoor for class 7: 100%|██████████| 10/10 [01:12<00:00,  7.26s/it]\n",
      "Generating backdoor for class 8: 100%|██████████| 10/10 [01:12<00:00,  7.28s/it]\n",
      "Generating backdoor for class 9: 100%|██████████| 10/10 [01:10<00:00,  7.09s/it]\n"
     ]
    }
   ],
   "source": [
    "defence_cleanse.mitigate(clean_x_test, clean_y_test, mitigation_types=[\"pruning\", \"filtering\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
